【问题标题】:sizeof() part of a C struct - sort ofC 结构的 sizeof() 部分 - 有点
【发布时间】:2011-02-02 20:12:55
【问题描述】:

我想获取一些 C 结构中的部分数据以部分序列化/反序列化它们,将字节从内存写入磁盘,反之亦然。

这些结构是事先不知道的,它们是用我自己的 C 代码生成器(以及将序列化它的代码)动态构建的。可序列化的字段将放置在结构的开头。

假设一个结构体有 4 个字段,前两个要序列化:

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;

我计划获取指向结构变量的指针并写入/读取覆盖这两个第一个字段 (x1, x2) 的 n 字节。我认为我不需要担心对齐/打包,因为我不打算让序列化在不同的编译中存活下来(预计只有一个唯一的可执行文件来读取/写入数据)。而且,由于我的目标是广泛的编译器架构,我不想对对齐打包或编译器特定的技巧进行假设。

然后,我需要计算字节数。而且我不能只做sizeof(st.x1)+sizeof(st.x2) 因为alingment-padding。所以,我打算减去指针,从结构的开头到第一个“非持久”字段:

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

这似乎有效。并且可以放在宏中。

(作为记录:我还尝试编写另一个不需要结构实例的宏,例如this,但在这里似乎不可能 - 但这对我来说并不重要)。

我的问题:这是正确和安全的吗?你能看到任何潜在的陷阱,或者更好的方法吗?

除此之外:C 标准(和事实上的编译器)是否假定结构字段在内存中的顺序与它们在源代码中定义的顺序相同?这可能是一个愚蠢的问题,但我想确定......

更新:从答案和我自己的发现中得出的一些结论:

  1. 我的方法似乎没有问题。特别是,C 规定结构字段永远不会改变顺序。

  2. 也可以(如 aswer 所建议的)从最后一个持久字段计数并添加其大小:(char*)&myst.x2 + sizeof(&myst.x2) - (char*)(&myst)。这将是等效的,只是它不包括最后一个字段的填充字节(如果存在)。一个非常小的优势 - 一个非常小的劣势,就是不那么简单。

  3. 但接受的答案,offsetof,似乎比我的建议更可取。它是清晰的表达和纯粹的编译时,它不需要结构的实例。它进一步似乎是标准的,可在任何编译器中使用。 如果不需要编译时构造,并且有可用的结构实例(如我的场景),那么两种解决方案本质上是等效的。

【问题讨论】:

标签: c struct


【解决方案1】:

您看过offsetof 设施吗?它返回成员从结构开始的偏移量。所以offsetof (st, x2) 返回x2 从结构开始的偏移量。因此,在您的示例中,offsetof (st, x2) + sizeof(st.x2) 将为您提供序列化组件的字节数。

这与您现在所做的非常相似,您只需忽略 x2 之后的填充并使用很少使用的 C 片段。

【讨论】:

  • 很好的建议,不知道(或记得?)它。可悲的是,它似乎并非无所不在。尤其是 MINGW gcc,缺少它。但是在其他编译器中查看它的定义是很有价值的。
  • @leonbloy: offsetof() 是标准 C,但您必须包含 <stddef.h> 才能获得它。
  • 我知道,但编译器在遵守标准方面各不相同。 MINGW 的 gcc 3.4.5(我第一次尝试的那个,随便)没有将它包含在它的 stddef.h 中。我会在我的帖子中添加一些评论。
  • @leonbloy:它是原始“ANSI C”(C89)的一部分,其合规性通常非常好。 (顺便说一下,我本地 MinGW gcc 3.4.5 安装中的stddef.h确实包含它)。
【解决方案2】:

C 保证这种行为。它旨在允许原始多态性。考虑:

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.

【讨论】:

    【解决方案3】:

    C 编译器可能会插入填充字节以进行对齐,但可能不会重新排序结构变量。

    一种更简洁的方法可能是只为 sizeof() 目的定义第二个结构,其中包括您想要的结构的开始变量。编译器会保证2个具有相同变量顺序相同的struts会以相同的方式布局。

    【讨论】:

    • +1 不错的建议。但在我的场景中——很多结构体和很多字段——这种方法会导致过于侵入(如果我重新定义结构体,嵌套),或者引入太多混乱(如果我为每个原始结构创建一个双胞胎结构)。
    【解决方案4】:

    你不妨看看protobuf;它似乎以便携的方式做你想做的事。

    【讨论】:

      【解决方案5】:

      我投票支持 KISS 原则。逐个元素写入文件,保证没有编译器依赖项。

      【讨论】:

        猜你喜欢
        • 2010-12-26
        • 1970-01-01
        • 2018-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-29
        • 2018-05-01
        相关资源
        最近更新 更多