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

发布于 2024-10-31 04:45:53 字数 1393 浏览 0 评论 0原文

我在 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;

和函数:

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;
}

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

所以当我得到255时,这显然是错误的.. 我怀疑这与结构填充有关。

我怎样才能做到这一点,使结构中字段的顺序无关紧要?
或者我需要遵循哪些标准才能使事情正常进行?
我需要一次 fwrite/fread 一名成员吗? (出于效率考虑,我想避免这种情况)
我需要先序列化为数组而不是文件吗? (我希望不是..因为这意味着由于分配的数组而需要事先知道所有结构的大小 - 这意味着需要为每个非简单结构创建一个函数来了解其大小进行额外的工作)

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

I have the following structs in 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;

and the functions:

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;
}

When I load the struct User, and I print that user (loaded from file), the field "last" of placesHistory has the value of 255 or -1, depending on the order of the fields of the PlacesHistory structure. But The User I saved had -1 on that member.

So when i get 255, it is obviously wrong..
I suspect this has to do about struct padding.

How can I do this in such a way that the order of fields in the structure doesn't matter?
Or which criteria do I need to follow to make things work right?
Do I need to fwrite/fread one member at a time? ( I would like to avoid this for efficiency matters )
Do I need to serialize to an array first instead of a file? (I hope not .. because this implicates to know the size of all my structures beforehand because of the mallocated array- which means extra work creating a function for every non simple structure to know it's size)

Note: *Size are defined constants
Note2: DynamicArray is a pointer to another structure.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦里兽 2024-11-07 04:45:53

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

您只需写出 sizeof(User) - sizeof(DynamicArray) 并以相同的方式读回即可。当然,只有当您的结构定义和编译器不改变时,这才会兼容。如果您不需要程序一个版本的序列化数据与另一版本兼容,那么上述方法应该可以工作。

Yes, it probably has to do with padding in front of either totalPoints or history.

You can just write out sizeof(User) - sizeof(DynamicArray) and read back in the same. Of course this will only be compatible as long as your struct definitions and compiler don't change. If you don't need serialized data from one version of your program to be compatible with another version, then the above should work.

莫言歌 2024-11-07 04:45:53

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

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

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

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

Why are you adding up all elements individually? That's just adding a lot of room for error. Whenever you change your structure, your code might break if you forgot to change all the places where you add the size up (in fact, why do you add it up each time?).

And, as you suspected, your code doesn't account for structure padding either, so you may be missing up to three bytes at the end of your data block (if your largest element is 4 bytes).

Why not sizeof(User) to get the size of the amount of data you're reading/writing? If you don't want parts of it saved (like requests), then use a struct inside a struct. (EDIT: Or, like rlibby suggested, just subtract the sizeof of the part you don't want to read.)

My guess is that your strings sizes are not divisible by 4, so you are 3 bytes short, and as such, it's possible that you were supposed to read "0xffffffff" (=-1) but ended up just reading "0xff000000" (=255 when using little endian, and assuming that your structure was zeroed out initially).

稳稳的幸福 2024-11-07 04:45:53

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

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

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

要解决此问题,请在之前使用#pragma pack(push,1),在之后使用#pragma pack(pop)

padding may be your problem because

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

so la last member (and last in struct) remain unitialized. To check this do a memset(,0,sizeof(User)) before reading from file.

To fix this use #pragma pack(push,1) before and #pragma pack(pop) after

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文