C 结构中的填充
我知道有 现在,我不明白下面的例子:C 结构中的填充,c,struct,C,Struct,我知道有 现在,我不明白下面的例子: typedef struct { -->**shouldn't it be 12 bytes** int a; char *str; } TestS; TestS s; int main(int argc, char *argv[]) { printf("An int is %lu bytes\n", sizeof( int )); -->4 printf("A Char * is %lu bytes\n
typedef struct { -->**shouldn't it be 12 bytes**
int a;
char *str;
} TestS;
TestS s;
int main(int argc, char *argv[]) {
printf("An int is %lu bytes\n", sizeof( int )); -->4
printf("A Char * is %lu bytes\n", sizeof( char *)); -->8
printf("A double is %lu bytes\n", sizeof( double )); -->8
printf("A struct is %lu bytes\n", sizeof s); -->why 16?
return 0;
}
首先,我认为它可能与8*N字节对齐(因为我使用的是ubuntu-64),所以我尝试了更多的结构
typedef struct {
int i;
char *str;
} stru_12;
typedef struct {
int i;
char *str;
char c;
} stru_13;
typedef struct {
int i;
char str[7];
} stru_11;
typedef struct {
char *str;
double d;
} stru_16;
stru_12 test12;
stru_13 test13;
stru_11 test11;
stru_16 test16;
int main (int argc, char *argv[]) {
printf("A test12 is %lu bytes, address is %p\n", sizeof test12, &test12);
printf("A test13 is %lu bytes, address is %p\n", sizeof test13, &test13);
printf("A test11 is %lu bytes, address is %p\n", sizeof test11, &test11);
printf("A test16 is %lu bytes, address is %p\n", sizeof test16, &test16);
}
结果:
test12是16字节,地址是0x601060
test13是24字节,地址是0x601090
test11是12个字节,地址是0x601080
test16是16字节,地址是0x601070
对不起,这么久了
我的问题是:
- 为什么test12(int+char*)是16字节,test13(int+char*)是24字节?(似乎8*N是受欢迎的,但允许12字节)
- 为什么结构地址的差异是16个寻址单元(更多填充?)
指针必须正确对齐,CPU才能使用它们 在C/C++中,结构必须在数组中工作,因此在这方面对结构的末尾进行了填充
struct A
{
char a;
// 7 bytes of padding
char *p;
char b;
// 7 bytes of padding
};
A array[3]; // the last padding is important to do this
在这种结构中,p
必须对齐,以便处理器可以读取指针而不产生错误(32位INTEL处理器可以设置为对未对齐的数据无错误,但这不是一个好主意:它速度较慢,并且经常会跳过错误。64位处理器在这方面有更多限制。)
因此,由于是64位的,指针是8字节,指针前面的对齐必须是8的倍数
类似地,结构的总大小必须是结构中最大类型的倍数,这里是8,因此它在末尾填充到下一个8字节
不过,实际上只有两种情况需要担心:(1)创建一个要保存在文件中的结构,(2)创建一个将大量分配的结构。在所有其他情况下,不用担心。第二个问题是实现定义的(事实上,第一个问题也是如此,但我将向您展示为什么要获得不考虑的间距)。您的平台显然是64位的,因此您的数据指针也是64位的。有了这些,我们就可以窥视结构了
stru_12
typedef struct
{
int i;
char *str;
} stru_12;
这是对齐的,因此str
始终位于8字节边界上,包括在连续序列(数组)中。为此,在i
和str
之间引入了4个字节的填充
0x0000 i - length=4
0x0004 pad - length=4
0x0008 ptr - length=8
======================
Total 16
这些数组的8字节边界上总是有ptr
,前提是数组从所述边界开始(它将从该边界开始)。由于在i
和str
之间添加了填充,也使结构大小达到了8的倍数,因此除此之外不需要额外的填充
stru_13
现在,考虑一下这也是如何实现的:
typedef struct
{
int i;
char *str;
char c;
} stru_13;
同样的填充将在i
和str
之间应用,以再次将str
放置在8字节边界上,但是添加c
会使事情复杂化。为了实现指针始终位于8字节边界(包括这些结构的序列/数组)上的目标,该结构需要尾部填充,但需要多少?我希望很明显,整体结构大小需要是8的倍数,以确保任何嵌入式指针(也是8的倍数)正确对齐。在这种情况下,将添加七个字节的尾部填充,以使大小达到24个字节:
0x0000 i - length=4
0x0004 pad - length=4
0x0008 ptr - length=8
0x0010 c - length=1
0x0011 pad - length=7
======================
Total 24
stru_13(双份) 所以试试这个。您认为我们以前拥有的相同字段,但顺序不同,会产生什么结果:
typedef struct
{
char *str;
int i;
char c;
} stru_13;
好吧,我们知道我们想要8字节边界上的str
,4字节边界上的i
,坦率地说,我们对c
(总是一个伴娘)毫不关心:
通过你的测试程序运行它,你会看到它像我们上面所说的那样成功。它减少到16字节。我们所做的只是将订单更改为一个空间友好的布局,该布局仍然支持我们的需求,并且我们将默认表示形式减少了8个字节(与先前布局相比是原始结构的三分之一)。要说这是一件重要的事情,要从这一切中去掉,这是一种轻描淡写的说法。这段代码非常嘈杂。您是否可以删除所有的typedef和变量,改为使用
sizeof(struct stru_12)
等?减少视觉混乱。谢谢你的建议,但我需要地址。任何解?24==8*3。8是校准单元,而不是12。地址之间的差异没有意义,除非它们是同一数组中元素的地址。@Tony,一般来说,每个memeber都应该适当对齐,以便整个struct对象,因为它可以在数组中使用。@Tony:地址相对来说没有意义,所以我不想麻烦它,但如果你想保持原样,那很好。由你决定。(当然还有你的问题!)这是一个很好的答案,但是12字节test11的情况是什么呢?@martinstru_11
没有指针或双指针,所以8字节的边界就不存在了,但是理想的地址访问i
(32位int
)应该把它放在4字节的边界上,并且再次按顺序保持它。为了实现这一点,在结构尾部添加了一个额外的填充。结果是一个12字节的长度,i
总是落在一个4字节的边界上(当然,假设它从一开始,它会这样做)。在i
的类型中切换long
和short
,看看会发生什么。@martin还值得注意的是,当您将成员从结构中的最大值到最小值排序时,情况可能会发生很大的变化。尤其值得玩玩。@Tony关键是你不必这么做。编译器正在为您执行此操作。大多数情况下你都很好。如果您需要压缩每页更多的项目,以更好地利用
typedef struct
{
char *str;
int i;
char c;
} stru_13;
0x0000 ptr - length=8
0x0008 i - length=4
0x000c c - length=1
0x000d pad - length=3
======================
Total 16