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

时间:2014-03-24 00:16:43

标签: c struct alignment standards

让我们假设我想读/写一个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的数组,之后可能会将它们逐个转换为字符。有更简单的方法吗

3 个答案:

答案 0 :(得分:6)

C11(latest freely available draft)仅表示“结构对象中可能有未命名的填充,但不是在其开头”(§6.7.2.1¶15)和“可能在结尾处有未命名的填充”结构或联合“(§6.7.2.1¶17)。它对结构中的填充没有进一步的限制。

平台ABI可能对填充有更严格的要求,但依赖于此将是特定于平台的,因为其他平台可能具有其他填充要求。 x86-64 ABI for Unix/Linux给出char 1个字节对齐,并指定:

  

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

     

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

     

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

     
     

4 对齐要求允许在阵列上操作时使用SSE指令。   编译器通常不能计算可变长度数组(VLA)的大小,但它是ex-   预计大多数VLA将需要至少16个字节,因此理所当然地要求VLA具有   至少16字节对齐。

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

如果您想要便携,在一次通话中阅读结构时,您可能需要查看readv。这是vectored or scatter/gather I/O operation,它允许您指定要读入的数组和长度数组。例如,对于这种情况,您可以写:

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的数组,之后可能会将它们逐个转换为字符。有更简单的方法吗

C规范不保证uint8_t可用:“typedef名称uintN_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,并且称之为好。

答案 1 :(得分:3)

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

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

答案 2 :(得分:0)

C99和C11标准草案在 13 段落中的6.7.2.1 结构和联合说明符一节中说明(C11中的段落 15 ):

  

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

并在段落 15 (C11中段落 17 ):

  

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