结构体类型
结构体对象
结构体对象的定义形式
定义结构体对象有三种形式:
先定义结构体类型,再定义结构体对象
1
2
3
4
5struct DATE{
int year, month, day;
};
DATE a, b; // C++的方式
struct DATE c, d; // C语言的方式定义结构体类型的同时定义结构体对象
1
2
3struct DATE{
int year, month, day;
}d1, d2;直接定义结构体对象
1
2
3struct {
int year, month, day;
}d1, d2;
结构体对象的内存形式
结构体各成员是根据在结构体定义时出现的顺序依次分配空间的,其内存长度是各个成员内存长度之和,推荐使用sizeof
运算,由编译器自动确定内存长度。
注意:在有的编译器中,sizeof
得到的结构体内存长度可能比理论值大。如:1
2
3
4
5
6
7
8
9
10
11
12struct A
{
int a; // 4 字节
char b; // 1 字节
short c; // 2 字节
};
struct B
{
char b; // 1 字节
int a; // 4 字节
short c; // 2 字节
};
这两个结构体类型成员相同(仅顺序不同),理论上它们的内存长度都是4+1+2=7
。但实际上sizeof(A)
的结构为8,sizeof(B)
的结构为12。
原因:为了加快数据存储的速度,编译器默认情况下会对结构体成员和结构体本身(其他数据成员也是如此)存储位置进行处理,使其存放的起始位置是一定字节数的倍数,而不是顺序存放,称为字节对齐。
设对齐字节数为n(n=1, 2, 4, 8, 16)
,每个字节内存长度为Li
,Max(Li)
为最大的成员内存长度。字节对齐的规则是:
- 结构体对象的起始位置能够被
Max(Li)
所整除; - 结构体中每个成员相对于起始地址的偏移量,即对齐值应是
min(n, Li)
的倍数。若不满足对齐值的要求,编译器会在成员之间填充若干个字节,称为internal padding; - 结构体的总长度值应是
min(n, Max(Li))
的倍数,若不满足总长度的要求,编译器在为最后一个成员分配空间后,会在其后填充若干个字节,称为trailing padding。
VC默认的对齐字节数为n=8
,则A与B的内存长度分析如下:
- A的第一个成员a为
int
,对齐值min(n, sizeof(int))=4
,成员a相对于结构体起始地址从0偏移开始,满足4字节对齐要求;- 第二个成员b为
char
,对齐值min(n, sizeof(char))=1
,b紧接着a后面从偏移4开始,满足1字节对齐要求;- 第三个成员c为
short
,对齐值min(n, sizeof(short))=2
,如果c紧接着b后面从偏移5开始就不满足2字节对齐要求,因此需要补充一个字节,从偏移6开始存储。结构体A的内存长度为4+1+1(补充)+2=8。
- B的第一个成员b为
char
,对齐值min(n, sizeof(char))=1
,成员b相对于结构体起始地址从0偏移开始,满足1字节对齐要求;- 第二个成员a为
int
,对齐值min(n, sizeof(int))=4
,如果a紧接着b后面从偏移1开始,不满足4字节对齐要求,因此补充3个字节,从偏移4开始存储;- 第三个成员c为
short
,对齐值min(n, sizeof(short))=2
,c紧接着a后面从偏移8开始,满足2字节对齐要求。- 则当前总的内存长度为1+3(补充)+4+2=10,由于n大于最大的成员内存长度4,故结构体长度应是4的倍数,因此最后需要再补充2个字节。
结构体B的内存长度为1+3(补充)+4+2+2(补充)=12。
使用预处理命令#progma pack(n)
可以设定对齐字节数n(n=1, 2, 4, 8, 16)
。例如:1
2
3
4
5
6
7
8
9
struct A
{
int a; // 4 字节
char b; // 1 字节
short c; // 2 字节
};
此时sizeof(A)
的结果为7。