C中结构的大小与重新排列的变量不同

C中结构的大小与重新排列的变量不同,c,struct,padding,C,Struct,Padding,当我移动结构变量时,我试图了解为什么结构的大小不同,我知道其中涉及到填充,但不清楚它在后台做了什么 struct test1 { long y; int a; short int b; short int t; } sizeof(struct test1) = 16 struct test2 { long y; short int b; int a; short int t; } sizeof(struct test2) =

当我移动结构变量时,我试图了解为什么结构的大小不同,我知道其中涉及到填充,但不清楚它在后台做了什么

struct test1 {
    long y;
    int a;
    short int b;
    short int t;
}

sizeof(struct test1) = 16

struct test2 {
    long y;
    short int b;
    int a;
    short int t;
}

sizeof(struct test2) = 24

struct test3 {
    int a;
    long y;
    short int b;
    short int t;
}

sizeof(struct test3) = 24
我得到test1的大小是8+(4+2+2),没有填充, 但是我不明白为什么test2不返回相同的结果,没有填充的8+(2+4+2)

第三个测试3我们看到int需要4字节+4个填充,long需要8字节,short int需要2字节+2字节+4个填充

如果test3可以使两个短int成为连续的,为什么test2不能使短int、int和短int成为连续的

此外,这是否意味着我们应该始终确保对结构成员进行重新排序以最小化填充

那么,与test2和test3相比,test1的声明总是更好

编辑: 作为后续行动

struct test4 {
    char asdf[3];
    short int b;
};

sizeof(struct test4) = 6

当数组大小为3的字符被填充到4字节时,短int不应该被填充到4字节吗?

由于填充和对齐,大小不同

此外,这是否意味着我们应该始终确保对结构成员进行重新排序以最小化填充

可能不需要,只需要packed属性,它们都是相同的16字节。假定gcc

struct __attribute__((__packed__)) test2 {
    long y;
    short int b;
    int a;
    short int t;
};
但是,请注意,在大小和速度之间有一个折衷:

填充结构物时,不插入这些填充物。这个 编译器必须生成更多代码(运行速度较慢)才能提取 不结盟的数据成员,并向他们写信


背景中发生的是对齐。对齐要求数据类型具有可被某个单元整除的地址。如果对齐单元是类型本身的大小,那么这是符合C语言的实现中存在的最严格的对齐

C编译器倾向于确保结构布局中的某些对齐,即使需求不是来自目标硬件

如果我们有一个
long
,也就是说,4个字节,后跟一个两字节
short
,那么
short
可以紧跟在
long
之后,因为对于一个两字节的类型来说,4字节的偏移量已经足够多了。这两个成员之后的偏移量为6。但是,编译器不认为6是4字节<代码> int <代码>的合适对齐方式;它需要4的倍数。插入两个字节的填充以将
int
移动到偏移量8

当然,实际数字是特定于编译器的。您必须知道类型的大小以及对齐要求和规则

此外,这是否意味着我们应该始终确保对结构成员进行重新排序以最小化填充

如果最小结构尺寸在应用程序中很重要,则必须将成员从最严格对齐排列到最不严格对齐排列。如果最小结构尺寸不重要,那么您就不必关心这一点

其他关注点可能也有影响,比如与外部强加布局的兼容性

或增量增长。如果一个公共使用的结构(由编译代码的多个实例引用,如可执行文件和动态库)在多个版本中随着时间的推移而维护,通常只能在最后添加新成员。在这种情况下,即使我们愿意,我们也无法获得最小尺寸的最佳订单

当数组大小为3的字符填充为4字节时,short int不应该填充为4字节吗

否,因为
char[4]
数组后面的一个字节的填充将偏移量增加到4。该偏移量的对齐程度足以放置两字节短字符。此外,在短距离后不需要填充。为什么?短路后的偏移量为6。结构中最严格对齐的构件如此短,对齐要求为2。6可被2整除

在这种情况下,在两个字节的short之后需要对齐:
struct{long x;short y;}
。假设
long
为4字节。或者,让我们把它设为8,没关系。如果我们将2字节
放在8字节
之后,则大小为
10
。如果我们声明此结构的数组
a
,则会出现问题,因为
a[1]。x
将位于距数组底部的偏移量
10
x
未对齐。最严格对齐的结构构件是
x
,其对齐要求为(比如)8,与其尺寸相同。因此,为了阵列对齐,必须对结构进行填充,使其大小可被8整除。因此,需要在末尾添加6个字节的填充,才能将大小增加到16

基本上,在成员之前填充是为了其自身的对齐,而在结构末尾填充是为了确保所有成员在一个数组中对齐,这是由最严格对齐的成员驱动的

在某些平台上,校准是硬硬件要求!例如,如果在不能被四整除的地址访问一个四字节的数据类型,就会发生CPU异常。在某些这样的平台上,CPU异常可以由操作系统处理,操作系统在软件中实现不对中访问,而不是向进程传递潜在的致命信号。这种访问非常昂贵,可能需要几百条指令。我似乎记得,在Linux的MIPS端口中,这是一个每个进程的选项:对于依赖它的一些非移植程序(例如为英特尔x86开发的程序),可以启用处理未对齐异常,但对于由于某些损坏错误而只执行未对齐访问的程序,则不能启用(例如,未初始化的指针碰巧指向有效内存,但指向未对齐的地址)

在某些平台上,硬件可以处理未对齐的访问,但与对齐的访问相比,成本仍然较低。例如,可能需要进行两次内存访问,而不是一次

C编译器
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|           y           |     a     |  b  |  t  |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|           y           |  b  |  p  |     a     |  t  |        p        |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|     a     |     p     |           y           |  b  |  t  |     p     |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+