开发者

sizeof() part of a C struct - sort of

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.


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.


C guarantees this kind of behaviour. It's intended to allow primitive polymorphism. Consider:

struct X {
   int a;
};
struct Y {
   int a;
   int b;
};
void foo(X* x) {
   x->a = 10;
};
Y y;
foo((X*)&y); // Well defined behaviour- y.a = 10.


The C complier may insert padding bytes for alignment, but may not reorder the struct variables.

A cleaner way might be to just define a 2nd struct for sizeof() purposes, that includes the beginning variables of the struct you want. The compiler will guarantee that 2 struts that have the same variables in the same order will layout in the same way.


You may wish to take a look at protobuf; it seems to do what you want in a portable manner.


I vote for the KISS principle. Write to the file element by element and you're guaranteed no compiler dependencies.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜