C标准中char数组结构成员的对齐方式

让我们假设我想读/写一个tar文件头。 考虑到标准C(C89,C99或C11),char数组在结构上是否有任何特殊处理,关于填充? 编译器可以为这样的结构添加填充:

struct header { char name[100]; char mode[8]; char uid[8]; char gid[8]; char size[12]; char mtime[12]; char chksum[8]; char typeflag; char linkname[100]; char tail[255]; }; 

我已经看到它也用在网络上的代码中。 只是自由,将这个结构写入一个块中的文件,假设不会有任何填充。 当然也假设CHAR_BITS == 8 。 我认为这样的C代码是如此常见,标准会处理这种情况,但我无法找到它,也许我不会是一个好律师。

编辑

接受的答案将根据C标准之一给出严格或最严格的可移植实现,这使我可以使用标准库字符串函数处理这些字段。 考虑CHAR_BITS和所有。 我认为需要为此读取512 uint8_t的数组,之后可能会将它们逐个转换为chars。 有更简单的方法吗

C11( 最新的免费草案 )仅说“结构对象中可能有未命名的填充,但不是在其开头”(§6.7.2.1¶15)和“结构或联合的末尾可能有未命名的填充” “(§6.7.2.1¶17)。 它对结构内的填充没有进一步的限制。

平台ABI可能对填充有更严格的要求,但取决于此将是平台特定的,因为其他平台可能具有其他填充要求。 Unix / Linux的x86-64 ABI提供了char 1字节对齐,并指定:

结构和联合体假定其最严格对齐的组件对齐。 每个成员都被分配到具有适当对齐的最低可用偏移量。 任何对象的大小始终是对象对齐的倍数。

数组使用与其元素相同的对齐方式,除了长度至少为16个字节的本地或全局数组变量或C99可变长度数组变量始终具有至少16个字节的对齐方式4

结构和联合对象可能需要填充以满足大小和对齐约束。 任何填充的内容都是未定义的。


4对齐要求允许在arrays上操作时使用SSE指令。 编译器通常不能计算可变长度数组(VLA)的大小,但预计大多数VLA需要至少16个字节,因此要求VLA至少具有16个字节的对齐是合乎逻辑的。 。

这似乎意味着在这个平台上,结构中不会有填充。 但是,有些情况下,数组变量具有更严格的对齐限制,以便能够与向量指令一起使用; 其他平台也可能对arrays结构成员施加此类限制。

如果您想要便携,在一次通话中阅读结构时,您可能需要查看readv 。 这是一个向量或分散/聚集I / O操作 ,它允许您指定要读入的数组和长度数组。 例如,对于这种情况,您可以写:

 struct header h; struct iovec iov[10]; iov[0].iov_base = &h.name; iov[0].iov_len = sizeof(h.name); iov[1].iov_base = &h.mode; iov[1].iov_len = sizeof(h.mode); /* ... etc ... */ bytes_read = readv(fd, iov, 10); 

请注意, readv在POSIX / Single Unix规范中定义,而不是在C标准中定义。 在标准C中,最简单的方法就是单独读取每个元素(即使提供了向量I / O,只需单独读取和写入每个元素可能会更加清晰,除非您绝对需要使用单个调用整个I / O操作)。

在您的编辑中,您写道:

接受的答案将根据C标准之一给出严格或最严格的可移植实现,这使我可以使用标准库字符串函数处理这些字段。 考虑CHAR_BITS和所有。 我认为需要为此读取512 uint8_t的数组,之后可能会将它们逐个转换为chars。 有更简单的方法吗

C规范不保证uint8_t可用:“typedef name uint N _t指定一个宽度为N且没有填充位的无符号整数类型….这些类型是可选的。” (C11草案,§7.20.1.1,¶2-3)。 但是,如果8位值可用,则char保证为8位值,因为它保证至少为8位,并且保证是不是位域的最小对象(§5.2.4.2) .1¶1):

下面给出的值应替换为适用于#if预处理指令的常量表达式。 此外,除了CHAR_BITMB_LEN_MAX ,以下内容应替换为与根据整数提升转换的对应类型的对象的表达式具有相同类型的表达式。 它们的实现定义值的大小(绝对值)应等于或大于显示的值,具有相同的符号。

– 不是位字段(字节)的最小对象的位数
  CHAR_BIT 8 

因此,如果您没有可用的8位字节,您将无法直接读取这些字段并从它们作为单独的数组元素访问八位字节; 您必须使用位移和屏蔽手动拆分单个字节。 但是,我知道没有现代架构缺少8位字节(对于通用计算,文件I / O完全是一个问题;某些DSP可能,但它们可能不会有标准的C文件I / O )。

如果你确实有一个8位字节,那么char保证是8位,所以除了使用uint8_t vs char清晰度之外没有太多的好处。 如果您真的担心,我只会确保您在构建过程中的某个位置检查CHAR_BIT为8,并将其称为好。

实际上,填充,名称修改等不受C标准的约束,但具体的ABI: http : //en.wikipedia.org/wiki/Application_binary_interface 。

有明确的标准如何填充数据类型,以便它们可以在不同的编译器之间共享。 您的手册页很可能会告诉您切换以更改ABI。

C99和C11标准草案见第6.7.2.1节第13段中的结构和联盟说明者 (C11第15段):

[…]结构对象中可能有未命名的填充,但不是在它的开头。

15段(C11第17段):

结构或联合的末尾可能有未命名的填充。