为什么标准不要求最小化填充结构成员?

时间:2018-06-01 18:44:46

标签: c padding

标准似乎没有对struct成员强加任何填充要求,即使它does prohibit reordering (6.7.2.1p6)。 C平台最小可能填充的可能性有多大,即,不添加确保下一个成员(或同一结构的实例,如果这是最后一个成员)所需的最小填充量足够对齐其类型?

标准是否明白不要求填充最小化?

我问,因为缺少填充保证似乎阻止我将序列化对象可移植地表示为结构(即使我将自己仅限于uint8_t数组作为成员,编译器似乎也可以添加填充在他们之间),我发现在那里必须求助于偏移算术有点奇怪。

1 个答案:

答案 0 :(得分:1)

  

C平台最低限度填充的可能性有多大,即,不添加确保下一个成员(或同一结构的实例,如果这是最后一个成员)所需的最小填充量足够对齐其类型?

基本上,"额外"填充可能允许重要的编译器优化。

不幸的是,我不知道是否有任何编制者确实这样做(因此无法对其发生的可能性做出任何估计)。

作为一个简单的例子,考虑一个32位或64位架构,其中ABI声明字符串文字和字符数组与32位或64位边界对齐。许多C库函数(也)由C编译器本身实现;见例如these lists for GCC。编译器可以跟踪参数以查看它们是否引用字符串文字或(字母数组的开头),如果是,则替换例如, strcmp()具有优化的内置版本(以32位为单位进行比较,而不是一次刻录)。

作为一个更复杂的示例,请考虑RISC硬件架构,其中未对齐的字节访问比对齐的本机字访问慢。 (例如,前者可以作为后者在硬件中实现,然后是位移。)这样的体系结构可以具有要求所有结构成员进行字对齐的ABI。然后,C编译器将需要添加超过最小的填充。

传统上,C标准委员会一直非常小心,不排除任何类型的硬件架构正确实现该语言。

  

标准是否明白不要求填充最小化?

C标准的目的通常是确保C代码在使用不同编译器编译时的行为方式相同,并允许在任何具有足够功能的硬件体系结构上实现该语言。从这个意义上说,标准不需要最小的填充是非常明智的,因为一些ABI可能因任何原因需要更多的填充。

随着Microsoft "extensions"的引入,C标准的目的已经发生了重大转变,将C绑定到C ++以确保C ++编译器可以编译C代码,与C ++编译的差异最小,并提供接口可以作为更安全的市场营销#34;实际目的是将开发人员分开并将其绑定到单个供应商实现。因为这与标准的先前目的相反,并且显然非明智标准化单一供应商功能,如fscanf_s(),而不标准化多供应商功能,如getline(),它可能无法在C标准的上下文中定义 sensible 的含义。它绝对不符合"良好的判断力&#34 ;;它现在可能指的是被感官感知到的感觉"。

  

我问,因为缺少填充保证似乎阻止我将序列化对象可移植地表示为结构

你正在犯一些C程序员犯的错误,一遍又一遍。结构适合表示序列化对象。由于C结构规则,不应该使用结构来表示网络对象或文件头。

相反,您应该使用简单的字符缓冲区和访问器函数(从缓冲区中提取或打包每个成员或字段)或转换函数(将缓冲区内容转换为结构,反之亦然)。

即使是经验丰富的程序员(如提问者)仍然更愿意使用结构,其根本原因在于访问者/转换涉及大量额外代码;让编译器这样做会更好:代码更少,代码更简单,更容易维护。

我同意。如果引入了新的关键字serialized_struct,它甚至会非常简单;引入一个序列化的数据结构,其中包含与传统C结构完全不同的成员规则。 (请注意,此支持根本不会影响链接,因此它实际上并不像人们想象的那么复杂。)可以使用其他属性或关键字来指定显式字节顺序,编译器将为我们执行所有转换详细信息无论编译器以何种方式最适合其编译的特定体系结构。这种支持只适用于新代码,但它在减少互操作性问题方面非常有用 - 而且它会使很多序列化代码更简单!

不幸的是,当你将C标准委员会的传统厌恶与添加新关键词结合起来,以及从互操作性到供应商锁定的总体方向变化时,根本没有机会将这样的事情包括在内C标准。

当然,正如评论中所描述的,有许多C库实现了一个序列化方案或其他。我甚至自己写了一些(尽管有相当特殊的用例)。一个明智的方法(意图差的双关语)将是挑选一个充满活力的方法(维护得很好,图书馆周围有一个活泼的社区),并使用它。