如果 char 在 int 之后,为啥要添加填充?

Posted

技术标签:

【中文标题】如果 char 在 int 之后,为啥要添加填充?【英文标题】:Why padding are added, if char comes after int?如果 char 在 int 之后,为什么要添加填充? 【发布时间】:2015-05-09 19:10:51 【问题描述】:

比如有一个结构

struct A

char a;
int i;
;

在这种情况下,我们有 a[1 byte] + padding[3 byte] + int[4 byte] = 8。

现在让我们对上面的结构进行一点更新,

struct A

int i;
char a;
;

在这种情况下 char 出现在 int 之后,不需要添加填充字节,这意味着 sizeof(A) = 5 字节,但在这种情况下,我也得到了 8 字节的结果。为什么?

好的,那么这个案子呢

struct s
   
       int b;
       double c;
       char a;
   ;

根据下面给出的逻辑,有一个:size = b[4 bytes] + padding[4 bytes] + c[8] + a[1] + padding[7 bytes to align with double] = 24, 但执行后我得到 16。这怎么可能?

【问题讨论】:

【参考方案1】:

在这种情况下char 出现在int 之后并且不需要添加填充字节,这意味着sizeof(A) = 5 字节,但在这种情况下我也得到了8 字节结果。为什么 ?

首先你需要了解为什么需要填充?Wiki 说:

数据结构对齐是数据在计算机内存中排列和访问的方式。它由两个独立但相关的问题组成:数据对齐数据结构填充当现代计算机读取或写入内存地址时,它将以字大小的块(例如 32 位系统上的 4 字节块)或更大的形式执行此操作。数据对齐意味着将数据放置在等于字大小的某个倍数的内存偏移处,由于 CPU 处理内存的方式,这会提高系统的性能。为了对齐数据,可能需要在最后一个数据结构的结尾和下一个数据结构的开头之间插入一些无意义的字节,这就是数据结构填充。

为了使大小为 4 的倍数(int 的对齐),第二个 sn-p 将用 3 字节填充。编译后,第二个 sn-p 将被填充以正确对齐

struct A

    int i;
    char a; 
    char Padding[3]; // 3 bytes to make total size of the structure 8 bytes
;    

编辑:永远记住结构填充的两条黄金法则:

仅当结构成员后跟具有较大对齐要求的成员或结构的末端时,才会插入填充。 最后一个成员填充了所需的字节数,因此结构的总大小应该是任何结构成员的最大对齐的倍数。

如果发生

struct s

    int b;
    double c;
    char a;
;  

对齐将发生在

struct s

    int b;             // 4 bytes. b is followed by a member with larger alignment.
    char Padding1[4];  // 4 bytes of padding is needed 
    double c;          // 8 bytes
    char d;            // 1 byte. Last member of struct. 
    char Padding2[7];  // 7 bytes to make total size of the structure 24 bytes 
;   

另请注意,通过更改结构中成员的顺序,可以更改保持对齐所需的填充量。这可以通过如果成员按降序对齐要求排序来完成。

struct s
 
    double c;   // 8 bytes
    int b;      // 4 bytes 
    char a;     // 1 byte. Only last member will be padded to give structure of size 16 
;   

【讨论】:

但是对吗,那个 double 可以从 offset 开始,它是 8 的倍数?在您的解释中,double 的偏移量等于 5。 @user2846015; struct s double c; int b; char a; ; 的大小是多少? 按此顺序将是 c[8] + b[4] + a[1] + padding[3 to align with double] = 16 @user2846015;为什么?不应该是24吗? @user2846015;等待。更新我的答案。【参考方案2】:

不仅struct 的每个成员都必须进行数据对齐,而且struct 本身也必须与struct 中最大成员的大小对齐。因此,将填充添加到struct A,使其大小应该是sizeof isizeof a 中较大者的倍数。

查看 C 常见问题解答here

【讨论】:

【参考方案3】:

如果要拥有一个结构数组,则数组中的所有元素必须具有相同的大小和对齐方式;这意味着对于数组中的事物,大小必须是对齐的倍数。拥有一个大小不是对齐倍数的结构唯一有用的情况是,如果它没有直接合并到另一个数组中,而是用作另一个结构的一部分。这种情况有时确实会发生,但还不够频繁,以至于在语言设计中值得特别注意。

【讨论】:

【参考方案4】:

编译器必须在结构末尾添加填充的原因是结构可以是数组的一部分,并且数组的每个元素都必须正确对齐。

您的平台似乎希望将 int 对齐到 4 个字节。

如果你声明一个struct A 的数组:

struct A array[2];

那么array[1] 的第一个int 成员也应该有4 个字节的对齐。因此,编译器会将您的 struct A 填充为 8 个字节来完成此操作,而如果它没有添加任何填充且 sizeof(struct A) 为 5 个字节,array[1] 将无法正确对齐。

(请记住,编译器不能在数组元素之间插入填充,填充必须是数组元素本身的一部分,因为在上述情况下sizeof array 必须与sizeof(struct A) * 2 相同)

【讨论】:

以上是关于如果 char 在 int 之后,为啥要添加填充?的主要内容,如果未能解决你的问题,请参考以下文章

在 C 中,为啥 sizeof(char) 为 1,而 'a' 是 int?

通过向字符串添加 char 来填充字符串

用 int main(void) 编译失败; main(int argc, char *argv[]) 成功。为啥?

为啥 main(int argc, char* argv[]) 需要两个参数? [复制]

将“argc”拼写为“argv”会导致 char ** 和 int 之间的比较 - 为啥?

用户代码可以安全地使用结构填充吗?