Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading and writing structures in C

Tags:

c

fortran

I know that structs in C may not be laid out memory as they are in the code. For example:

struct a {
     short x;
     int y;
};

assuming 2 byte shorts and 4 byte ints, may actually take 8 bytes in memory as the compiler wants to align the members on 4 byte boundaries ... so there is 2 bytes of slack between x and y.

This makes reading and writing structs unportable across language, compiler, and hardware. The only way to read and write them is member by member. Yes, Endianness is also an issue here and swapping must be done at the member level but lets assume this is not an issue.

Fortran has a 'sequence' specifier for derived types (structures) that tells the compiler to lay out the members in memory as they are given. This allows portable reading and writing of derived types.

My question is: Is there any way to do a similar thing in C in a portable (and maintainable) way?

like image 334
Mcfly Avatar asked Nov 30 '14 09:11

Mcfly


2 Answers

Yes, Fortran 2003 introduced the bind(C) specifier which tells the compiler to do exactly the same as the companion C compiler does

type, bind(C) :: a
  components
end type

Additionally, the iso_c_binding module (a subpart of the whole Fortran 2003 C interoperability) defines constants which help you connect the C and Fortran intrinsic types:

use intrinsic :: iso_c_binding, only: c_short, c_int

type, bind(C) :: a
  integer(c_short) :: x
  integer(c_int) :: y
end type

Such type is said to be interoperable with your C struct.

The C interoperability is very widely supported in compilers. It is very difficult to find a compiler which does not support this feature and is still supported by its vendor.

Stay away from sequence when mixing C and Fortran. Sequence types cannot be made interoperable according to the standard.

like image 96
Vladimir F Героям слава Avatar answered Nov 08 '22 13:11

Vladimir F Героям слава


There is no 100% clean solution that I know of. What I typically do is create two header files called pack_on.h and pack_off.h. To define a structure without padding:

#include <pack_on.h>

struct a {
    short x;
    int y;
} PACKED_STRUCTURE;

#include <pack_off.h>

The header files contain the pragmas or whatever is needed to satisfy your compiler, i.e. something like:

#ifdef _MSC_VER
#pragma pack(1)
#define PACKED_STRUCTURE /* nothing */
#endif

#ifdef __GNUC__
#define PACKED_STRUCTURE __attribute__(packed)
#endif

As you already said, when binary layout is an issue, endianness is typically also important. This is not addressed in the above solution.

Please note that it is also a bad idea to use packed structures throughout your entire code. The padding which is added by the compiler when you don't force these packed structures is useful for fast memory access.

In many applications it is best to use these packed structures only in one layer of your application which deals with reading and writing these packed structures, and copy them into properly aligned structures before passing them on into the core of your application for further processing.

like image 41
netstat Avatar answered Nov 08 '22 14:11

netstat