Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sizeof() part of a C struct - sort of

Tags:

c

struct

I want to grab part of the data inside some C structs to partially serialize/deserialize them, writing the bytes from memory to disk, and viceversa.

The structs are not known in advance, they are dynamically built with my own C code generator (as well as the code that will serialize it). The serializable fields will be placed at the beginning of the struct.

Let's say a have a struct with 4 fields, and the first two are to be serialized:

typedef struct {
   int8_t x1;   
   int32_t x2;   /* 1 + 4  = 5 bytes (if packed) */
   int8_t y1;
   int32_t  y2;   /* 1 + 4  +1 + 4 = 10 bytes (if packed) */ 
}  st;

I plan to grab the pointer to the struct variable and write/read the n bytes that cover those two first fields (x1, x2). I don't think I need to worry about alignment/packing because I don't intend the serialization to survive different compilations (only a unique executable is expected to read/write the data). And, as I'm targeting a wide scope of compilers-architectures, I don't want to place assumptions on alignment-packing or compiler specific tricks.

Then, I need to count bytes. And I can't just do sizeof(st.x1)+sizeof(st.x2) because of alingment-padding. So, I'm planning to substract pointers, from the start of the struct to the first "non persistent" field:

st myst;
int partsize = (char*)&myst.y1 - (char*)(&myst);
printf("partial size=%d (total size=%d)\n",partsize,sizeof(myst));  

This seems to work. And it can be placed in a macro.

(For the record: I tried also to write another macro that does not requrire an instance of the struct, something like this, but it doesnt seem possible here - but this does not matter me much).

My question: Is this correct and safe? Can you see any potential pitfall, or some better approach?

Among other things: Does C standard (and de-facto compilers) assume that the structs fields lay in memory in the same order as they are defined in source? This probably is a stupid question, but I'd want to be sure...

UPDATE: Some conclusions from the answers and my own findings:

  1. There seems to be no problem with my approach. In particular, C dictates that struct fields will never change order.

  2. One could also (as suggested by an aswer) count from the last persistent field and add its size : (char*)&myst.x2 + sizeof(&myst.x2) - (char*)(&myst) . That would be equivalent, except that it would include not the padding bytes (if present) for the last field. A very small advantage - and a very small disadvantage, in being less simple.

  3. But the accepted answer, with offsetof, seems to be preferable than my proposal. It's clear-expressive and pure compile-time, it does not require an instance of the struct. It further seems to be standard, available in any compiler. If one does not need a compile-time construct, and has an instance of the struct available (as is my scenario) both solutions are esentially equivalent.

like image 476
leonbloy Avatar asked Feb 02 '11 20:02

leonbloy


Video Answer


1 Answers

Have you looked at the offsetof facility? It returns the offset of a member from the start of a struct. So offsetof (st, x2) returns the offset of x2 from the start of the struct. So in your example offsetof (st, x2) + sizeof(st.x2) will give you the count of bytes of the serialized components.

This is pretty similar to what you are doing now, you just get to ignore the padding after x2 and to use a rarely used piece of C.

like image 117
sbass Avatar answered Oct 30 '22 01:10

sbass