C中的结构填充
Structures padding in C
int在我的机器上是4字节,long是8字节等
嘿,所以我在 C 中遇到了一件非常有趣的事情,并开始想知道结构如何在内部管理它们的数据。我以为它像数组一样工作,但是天哪,我错了。所以基本上,我认为里面的数据会自行总结,但我发现堆栈溢出时,由于处理器的架构要求,一些编译器可能会做一些优化。并且有对齐。我找到了两个关于对齐的链接,我想计算我的结构的大小并且我已经做了一些实验,但我想我在某些方面理解了这一点,而在某些方面则没有。这就是我想创建该主题的原因,因为我无法完全理解在这些主题中回答的人提供的一些示例。例如:
#include <stdio.h>
struct test {
char a;
char b;
int c;
long d;
int e;
};
int main(void){
printf("test = %d\n", sizeof(test));
return 0;
}
输出:
test = 24
我原以为编译器会像这样进行优化:
char a
是1个字节,char b
是1个字节,所以不需要对齐。 char b 是 1 个字节,int c
是 4 个字节,因此我们需要对齐 3 个字节。 int c
是4个字节,long d
是8个字节,所以我们需要对齐4个字节。 long d
是8个字节,int e
是4个字节,所以我们需要对齐4个字节。到目前为止,总大小为 29。用上限四舍五入到最接近的偶数得到 30。那为什么是 24?
我还发现 char a + char b
给出了一个等于 2 个字节的填充,所以我们只需要再对齐 2 个字节,因此也许这就是我犯错误的地方。另外,如果我添加更多变量:
#include <stdio.h>
struct test {
char a;
char b;
int c;
long d;
int e;
char f;
char g;
char h;
char i;
};
int main(void){
printf("test = %d\n", sizeof(test));
return 0;
}
输出:
test = 24
总大小还是24字节。但是如果我再添加一个变量:
#include <stdio.h>
struct test {
char a;
char b;
int c;
long d;
int e;
char f;
char g;
char h;
char i;
char j;
};
int main(void){
printf("test = %d\n", sizeof(test));
return 0;
}
输出:
test = 32
大小更改为总共 32 个字节。为什么?到底发生了什么?抱歉,如果这个问题的答案对您来说很明显,但我真的不明白。另外我不知道编译器之间是否有所不同,所以如果我没有提供一些信息,请告诉我,我会添加。
这一切都归结为对齐。编译器希望使每个元素与一个地址对齐,该地址是该项目大小的倍数,因为硬件可以通过这种方式最有效地访问它。 (在某些架构上,硬件只能 以这种方式访问它);不允许未对齐的访问。)
您的结构中有一个 long int
大小为 8 的元素,因此它的对齐将驱动其他所有元素。这是您的第一个结构的布局方式:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
0 | a | b | pad | c |
+---+---+---+---+---+---+---+---+
8 | d |
+---+---+---+---+---+---+---+---+
16 | e | padding |
+---+---+---+---+---+---+---+---+
因此,如您所见,大小为 24,包括两个不可见的、未命名的“填充”字段,分别为 2 字节和 4 字节。
结构填充和对齐可能会造成混淆。 (令人尴尬的是,我尝试了很多次才得到正确的答案。)幸运的是,您通常不必担心这些,因为这是编译器的问题,而不是您的问题。
您可以使用 offsetof
宏让编译器告诉您它是如何布局结构的:
int main(void){
printf("a @ %zd\n", offsetof(struct test, a));
printf("b @ %zd\n", offsetof(struct test, b));
printf("c @ %zd\n", offsetof(struct test, c));
printf("d @ %zd\n", offsetof(struct test, d));
printf("e @ %zd\n", offsetof(struct test, e));
printf("size = %zd\n", sizeof(struct test));
return 0;
}
在我的机器上(它的行为似乎与你的一样)打印:
a @ 0
b @ 1
c @ 4
d @ 8
e @ 16
size = 24
请注意,我使用 %zd
而不是 %d
,因为 sizeof
和 offsetof
给出的答案类型为 size_t
,而不是 [=21] =].
当您添加 char
个字段 f
、g
、h
和 i
时,它们可以放入第二个填充 space,而不会使整体结构变大。只有当您添加 j
时,它才会将内容推入另一个 8 字节块:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
0 | a | b | pad | c |
+---+---+---+---+---+---+---+---+
8 | d |
+---+---+---+---+---+---+---+---+
16 | e | f | g | h | i |
+---+---+---+---+---+---+---+---+
24 | j | padding |
+---+---+---+---+---+---+---+---+
int在我的机器上是4字节,long是8字节等
嘿,所以我在 C 中遇到了一件非常有趣的事情,并开始想知道结构如何在内部管理它们的数据。我以为它像数组一样工作,但是天哪,我错了。所以基本上,我认为里面的数据会自行总结,但我发现堆栈溢出时,由于处理器的架构要求,一些编译器可能会做一些优化。并且有对齐。我找到了两个关于对齐的链接,我想计算我的结构的大小并且我已经做了一些实验,但我想我在某些方面理解了这一点,而在某些方面则没有。这就是我想创建该主题的原因,因为我无法完全理解在这些主题中回答的人提供的一些示例。例如:
#include <stdio.h>
struct test {
char a;
char b;
int c;
long d;
int e;
};
int main(void){
printf("test = %d\n", sizeof(test));
return 0;
}
输出:
test = 24
我原以为编译器会像这样进行优化:
char a
是1个字节,char b
是1个字节,所以不需要对齐。 char b 是 1 个字节,int c
是 4 个字节,因此我们需要对齐 3 个字节。 int c
是4个字节,long d
是8个字节,所以我们需要对齐4个字节。 long d
是8个字节,int e
是4个字节,所以我们需要对齐4个字节。到目前为止,总大小为 29。用上限四舍五入到最接近的偶数得到 30。那为什么是 24?
我还发现 char a + char b
给出了一个等于 2 个字节的填充,所以我们只需要再对齐 2 个字节,因此也许这就是我犯错误的地方。另外,如果我添加更多变量:
#include <stdio.h>
struct test {
char a;
char b;
int c;
long d;
int e;
char f;
char g;
char h;
char i;
};
int main(void){
printf("test = %d\n", sizeof(test));
return 0;
}
输出:
test = 24
总大小还是24字节。但是如果我再添加一个变量:
#include <stdio.h>
struct test {
char a;
char b;
int c;
long d;
int e;
char f;
char g;
char h;
char i;
char j;
};
int main(void){
printf("test = %d\n", sizeof(test));
return 0;
}
输出:
test = 32
大小更改为总共 32 个字节。为什么?到底发生了什么?抱歉,如果这个问题的答案对您来说很明显,但我真的不明白。另外我不知道编译器之间是否有所不同,所以如果我没有提供一些信息,请告诉我,我会添加。
这一切都归结为对齐。编译器希望使每个元素与一个地址对齐,该地址是该项目大小的倍数,因为硬件可以通过这种方式最有效地访问它。 (在某些架构上,硬件只能 以这种方式访问它);不允许未对齐的访问。)
您的结构中有一个 long int
大小为 8 的元素,因此它的对齐将驱动其他所有元素。这是您的第一个结构的布局方式:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
0 | a | b | pad | c |
+---+---+---+---+---+---+---+---+
8 | d |
+---+---+---+---+---+---+---+---+
16 | e | padding |
+---+---+---+---+---+---+---+---+
因此,如您所见,大小为 24,包括两个不可见的、未命名的“填充”字段,分别为 2 字节和 4 字节。
结构填充和对齐可能会造成混淆。 (令人尴尬的是,我尝试了很多次才得到正确的答案。)幸运的是,您通常不必担心这些,因为这是编译器的问题,而不是您的问题。
您可以使用 offsetof
宏让编译器告诉您它是如何布局结构的:
int main(void){
printf("a @ %zd\n", offsetof(struct test, a));
printf("b @ %zd\n", offsetof(struct test, b));
printf("c @ %zd\n", offsetof(struct test, c));
printf("d @ %zd\n", offsetof(struct test, d));
printf("e @ %zd\n", offsetof(struct test, e));
printf("size = %zd\n", sizeof(struct test));
return 0;
}
在我的机器上(它的行为似乎与你的一样)打印:
a @ 0
b @ 1
c @ 4
d @ 8
e @ 16
size = 24
请注意,我使用 %zd
而不是 %d
,因为 sizeof
和 offsetof
给出的答案类型为 size_t
,而不是 [=21] =].
当您添加 char
个字段 f
、g
、h
和 i
时,它们可以放入第二个填充 space,而不会使整体结构变大。只有当您添加 j
时,它才会将内容推入另一个 8 字节块:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
0 | a | b | pad | c |
+---+---+---+---+---+---+---+---+
8 | d |
+---+---+---+---+---+---+---+---+
16 | e | f | g | h | i |
+---+---+---+---+---+---+---+---+
24 | j | padding |
+---+---+---+---+---+---+---+---+