sizeof(),C结构中的对齐方式:

时间:2018-06-12 10:55:22

标签: c struct alignment

前言 我的研究结构对齐。看了this个问题,this一个问题,还有this问题 - 但仍未找到答案。

我的实际问题:

这是我创建的代码段,以澄清我的问题:

#include "stdafx.h"
#include <stdio.h>

struct IntAndCharStruct
{
    int a;
    char b;
};

struct IntAndDoubleStruct
{
    int a;
    double d;
};

struct IntFloatAndDoubleStruct
{
    int a;
    float c;
    double d;
};

int main()
{
    printf("Int: %d\n", sizeof(int));
    printf("Float: %d\n", sizeof(float));
    printf("Char: %d\n", sizeof(char));
    printf("Double: %d\n", sizeof(double));
    printf("IntAndCharStruct: %d\n", sizeof(IntAndCharStruct));
    printf("IntAndDoubleStruct: %d\n", sizeof(IntAndDoubleStruct));
    printf("IntFloatAndDoubleStruct: %d\n", sizeof(IntFloatAndDoubleStruct));
    getchar();
}

它的输出是:

Int: 4
Float: 4
Char: 1
Double: 8
IntAndCharStruct: 8
IntAndDoubleStruct: 16
IntFloatAndDoubleStruct: 16

我在IntAndCharStructIntAndDoubleStruct中看到了对齐。

但我只是没有得到IntFloatAndDoubleStruct

简单地说:为什么不是sizeof(IntFloatAndDoubleStruct) = 24

提前致谢!

p.s:我正在使用Visual Studio 2017,标准控制台应用程序。

编辑: 根据评论,测试IntDoubleAndFloatStruct(元素的不同顺序)并在sizeof()得到24 - 如果答案会注意并解释这个案例,我会很高兴。

4 个答案:

答案 0 :(得分:6)

在您的平台上,以下内容为:intfloat的大小均为4.大小&amp; double的对齐要求是8。

我们从您显示的sizeof输出中了解到这一点。 sizeof (T)给出数组中类型T的两个连续元素的地址之间的字节数。所以我们知道对齐要求就像我上面所说的那样。 (注)功能

现在,编译器报告了IntFloatAndDoubleStruct的16。它有用吗?

假设我们在与16对齐的地址处有这样的对象。

  • int a因此在地址X与16对齐,所以它与4对齐就好了。它将占用字节[X,X + 4)
  • 这意味着float c可以从X + 4开始,它与4对齐,这对float来说很好。它将占用字节[X + 4,X + 8)
  • 最后,double d可以从X + 8开始,它与8对齐,这对于double来说很好。它将占用字节[X + 8,X + 16)
  • 这为下一个struct对象留下了X + 16,再次与16对齐。

所以以后没有理由启动任何成员,所以整个结构都适合16个字节。

(注)这不是严格正确的:对于这些中的每一个,我们知道大小和对齐都是&lt; = N,N是对齐要求的倍数,并且那里是没有N1&lt; N也适用于此。然而,这是一个非常精细的细节,为了清楚起见,答案只是假设原始类型的实际大小和对齐要求是不确定的,这是OP平台上最可能的情况。

答案 1 :(得分:5)

您的结构必须长8*N个字节,因为它的成员有8个字节(double)。这意味着结构位于内存中的地址(A)可被8整除(A%8 == 0),其结束地址将是(A + 8N),也可以被8整除。

从那里,您存储2个4字节变量(int + float),这意味着您现在占用了内存区域[A,A+8)。现在存储一个8字节的变量(double)。自(A+8) % 8 == 0 [自A%8 == 0]起,无需填充。因此,如果没有填充,则会获得4+4+8 == 16

如果您将订单更改为int -> double -> float,那么您将占用24个字节,因为double变量原始地址将无法被8整除,并且必须填充4个字节才能到达有效地址(结构也将在末尾有填充)。

|--------||--------||--------||--------||--------||--------||--------||--------|
|   each ||   cell ||  here  ||represen||-ts  4  || bytes  ||        ||        |
|--------||--------||--------||--------||--------||--------||--------||--------|

A        A+4       A+8      A+12      A+16      A+20      A+24                      [addresses]
|--------||--------||--------||--------||--------||--------||--------||--------|    
|   int  ||  float || double || double ||        ||        ||        ||        |    [content - basic case]
|--------||--------||--------||--------||--------||--------||--------||--------|

first padding to ensure the double sits on address that is divisble by 8
last  padding to ensure the struct size is divisble by the largest member's size (8)
|--------||--------||--------||--------||--------||--------||--------||--------|    
|   int  || padding|| double || double || float  || padding||        ||        |    [content - change order case]
|--------||--------||--------||--------||--------||--------||--------||--------|

答案 2 :(得分:0)

编译器将插入填充以保证每个元素的偏移量是其大小的某个倍数。

在这种情况下,int将在偏移= 0(相对于结构实例的地址),偏移= 4时为float,偏移= 8时为double,因为大小intfloat的加起来为8。

最后没有填充 - 结构的大小已经是16,这是double大小的倍数。

答案 3 :(得分:0)

首先,C不对结构内的成员施加任何对齐。这就是为什么很难说sizeof()为某个结构的实例返回的值。

C对结构成员施加的唯一限制是为第一个成员分配的地址是其对齐的倍数(这样该结构的向量能够作为指针访问+ sizeof()* index )。在您的情况下,IntFloatAndDoubleStruct必须以sizeof(int)的倍数对齐,因为a的类型为int

在某些情况下,内部成员分配了多个对齐的地址,以便更快地工作(因此插入了一些填充),但这不是由C强加的。因此编译器可能决定插入#pragma来更改算法用于分配结构。请参阅here

CIsForCookies所示,结构将与最大对齐的倍数对齐,以使这种结构的向量仍然使每个成员对齐。但这不是由C语言强加的,而是考虑到实现的执行,因为在addr_struct[i]+member_offset中这两个术语都可以被对齐整除。