结构填充在C结构序列化中的影响(保存到文件)

我在C中有以下结构:

typedef struct sUser { char name[nameSize]; char nickname[nicknameSize]; char mail[mailSize]; char address[addressSize]; char password[passwordSize]; int totalPoints; PlacesHistory history; DynamicArray requests; }User; typedef struct sPlacesHistory { HistoryElement array[HistorySize]; int occupied; int last; }PlacesHistory; 

和function:

 void serializeUser( User * user, FILE * fp ) { fwrite( user, nameSize + nicknameSize + mailSize + addressSize + passwordSize + sizeof( int ) + sizeof( PlacesHistory ), 1, fp ); serializeDynamicArray( user -> requests, fp ); } User * loadUser( FILE * fp ) { User * user = malloc( sizeof( User ) ); fread( user, nameSize + nicknameSize + mailSize + addressSize + passwordSize + sizeof( int ) + sizeof( PlacesHistory ), 1, fp ); user -> requests = loadDynamicArray( fp ); return user; } 

当我加载结构用户,并且我打印该用户(从文件加载)时,placesHistory的字段“last”的值为255或-1,具体取决于PlacesHistory结构的字段顺序。 但我保存的用户在该成员上有-1。

所以当我得到255时,显然是错误的……我怀疑这与struct padding有关。

我怎么能这样做,结构中的字段顺序无关紧要?
或者我需要遵循哪些标准才能使工作正常进行?
我是否需要一次fwrite / fread一个成员? (我想在效率方面避免这种情况)
我是否需要首先序列化为数组而不是文件? (我希望不会……因为这意味着事先知道我所有结构的大小,因为mallocated数组 – 这意味着额外的工作为每个非简单结构创建一个函数来知道它的大小)

注意:*大小是定义的常量
注2:DynamicArray是指向另一个结构的指针。

是的,它可能与totalPointshistory前面的填充有关。

你可以写出sizeof(User) - sizeof(DynamicArray)并回读相同的内容。 当然,只要结构定义和编译器不变,这只会兼容。 如果您不需要从一个版本的程序中获取序列化数据以与另一个版本兼容,那么上述内容应该可行。

为什么要单独添加所有元素? 这只是增加了很多错误的空间。 每当您更改结构时,如果您忘记更改添加大小的所有位置,您的代码可能会中断(事实上,为什么每次都要添加它?)。

而且,正如您所怀疑的那样,您的代码也没有考虑结构填充,因此您可能在数据块末尾缺少最多三个字节(如果您的最大元素是4个字节)。

为什么sizeof(User)不能获得您正在读/写的数据量的大小? 如果您不希望保存部分内容(如请求),则在结构中使用结构。 (编辑:或者,就像rlibby建议的那样,只需减去你不想阅读的部分的大小。)

我的猜测是你的字符串大小不能被4整除,所以你短3个字节,因此,你应该读“0xffffffff”(= -1)但最后只读“0xff000000”(=使用little endian时的255,并假设你的结构最初被清零了)。

填充可能是你的问题,因为

 nameSize + nicknameSize + mailSize + addressSize + passwordSize + sizeof( int ) + sizeof( PlacesHistory ) != sizeof( User ) 

所以最后一个成员(和结构中的最后一个)保持单元化。 要检查这一点,请在从文件读取之前执行memset(,0,sizeof(User))。

要解决此问题,请先使用#pragma pack(push,1),然后使用#pragma pack(pop)