对于结构体的空间对齐规则,我刚开始的理解是:系统对于每种不同类型的变量,都会分配相同的空间(有些系统会分配4个字节,有些系统会分配8个字节)
但是!!这个理解是大错特错的,因为所谓的结构体的空间对齐规则正确的理解应该是:
1.系统在为结构体变量分配空间的时候,先会判断结构体中那个类型的成员所占的空间大小最大并以那个字节作为空间对齐的字节。(若表述难理解,可参考以下例子)
2.每个变量的起始地址都能被自身的大小整除。
(若表述难理解,可参考以下例子)(本文中涉及到地址,一般都以最后两位来描述)
例子1:
1 #include<stdio.h>
2
3 struct napo{
4 int napo1;
5 char napo2;
6 int napo3;
7 char napo4;
8 }napolin;
9
10
11 int main(void)
12 {
13 printf("addr of int napo1:%p\n", &napolin.napo1);
14 printf("addr of char napo2:%p\n", &napolin.napo2);
15 printf("addr of int napo3:%p\n", &napolin.napo3);
16 printf("addr of char napo4:%p\n", &napolin.napo4);
17 printf("size of struct napolin:%d\n", sizeof(napolin));
18
19 return 0;
20 }
执行的结果:
addr of int napo1:0x8049720
addr of char napo2:0x8049724
addr of int napo3:0x8049728
addr of char napo4:0x804972c
size of struct napolin:16
这个例子是一个很典型的例子,可能就是这个例子会让大家误以为,所谓的对齐规则,就是无论对于什么类型的变量,系统都会给它分陪四个字节,从这个例子中可以看到,char 型的napo2的空间也有4个字节。这是因为:先根据上述的第1点,因为该结构体成员中占字节最大的类型是int,所以系统会先随机分配一个能被4整除的地址作为该结构体的首地址,用于存放第一个int变量napo1,因为int 刚好4个字节,所以可以看到char变量napo2的起始地址是napo1地址加上4,而根据上述的第2点,该地址当然也是能够被char变量napo2自身的大小1个字节整除的。关键是看第三个成员int类型的napo3,这里可以看到,napo3的地址是在napo2的首地址的基础上加上4,这是因为napo2只占了1个字节,而根据结构体的字节对齐规则,每个对齐的空间只有4个字节,所以剩下的3个字节已经放不下一个整型的napo3了,而且根据上述的第二点,每个变量的首地址要被自身大小的字节数整除,所以系统会自动偏移3个字节,把int类型的napo3放到了对于napo2地址来说偏移了4个字节的地址处。而char型的napo4当然也只需要一个字节,但是为了空间的对齐(4个字节),所以系统也给char类型分配了4个字节的空间,所以该结构体变量的大小一共是16个字节。用图可表示为:
|------int--------| 4字节
|-----char------| 4字节
|-----int---------| 4字节
|-----char------| 4字节
例子2:
1 #include<stdio.h>
2
3 struct napo{
4 char napo1;
5 char napo2;
6 int napo3;
7 int napo4;
8 }napolin;
9
10
11 int main(void)
12 {
13 printf("addr of char napo1:%p\n", &napolin.napo1);
14 printf("addr of char napo2:%p\n", &napolin.napo2);
15 printf("addr of int napo3:%p\n", &napolin.napo3);
16 printf("addr of int napo4:%p\n", &napolin.napo4);
17 printf("size of struct napolin:%d\n", sizeof(napolin));
18
19 return 0;
20 }
结果预测:如果系统真的是对于每个不同类型的变量都是分配相同的4个字节,那么这个结构体对象napolin的大小应该也是16各字节
实验结果:
addr of char napo1:0x8049720
addr of char napo2:0x8049721
addr of int napo3:0x8049724
addr of int napo4:0x8049728
size of struct napolin:12
??!!明明完全相同的四个结构提成员,只是顺序发生了变化,为什么创建出来的结构体对象大小居然只有12个字节呢?
同样原理,系统先会随机选择一个能被4整除的地址作为起始地址,因为char的大小是一个字节,所以无论什么地址都能够被它整除,所以char可以放在任何一个位置。所以可以看到在第一个4字节的空间内,两个char型变量napo1和napo2被存在了两个相邻的地址中(20和21),而剩下的两个字节已经放不下一个int类型napo3了,所以napo3的地址被分配为下一个能够被4整除的地址处,也就是24了,而napo4也就很容易理解了。这里加起来也就用了3个4个字节的空间,所以该结构体变量的大小是12个字节。用图描述为:
|-----------char-----------------| 1
|-----------char-----------------| 1
|----------------------------------| 1
|----------------------------------| 1 总共4字节
|-------------int----------------- | 4字节
|-------------int------------------| 4字节
例子3:
1 #include<stdio.h>
2
3 struct napo{
4 char napo1;
5 short napo2;
6 short napo3;
7 int napo4;
8 }napolin;
9
10
11 int main(void)
12 {
13 printf("addr of char napo1 :%p\n", &napolin.napo1);
14 printf("addr of short napo2 :%p\n", &napolin.napo2);
15 printf("addr of short napo3 :%p\n", &napolin.napo3);
16 printf("addr of int napo4 :%p\n", &napolin.napo4);
17 printf("size of struct napolin:%d\n", sizeof(napolin));
18
19 return 0;
20 }
实验结果:
addr of char napo1 :0x8049730
addr of short napo2 :0x8049732
addr of short napo3 :0x8049734
addr of int napo4 :0x8049738
size of struct napolin:12
同样道理,以4个字节作为一个对齐的空间,第一个空间只能放下一个char(1字节)和一个short(两字节),且short的地址要能被2整除,所以看到short类型的napo2的起始地址是32并不是31,而第二个对齐空间只能放下一个2个字节的short而放不下一个int了,所以总共就有三个4字节的对齐空间,所以该结构提变量的大小是12个字节。
既然有字节对齐这么一回事,当然也就有修改它的方法,下面就介绍一下:
方法一:在程序的开头,在宏定义的位置加上下面的代码
#pragma pack(1) //取消字节对齐
#pragma pack() //恢复字节对齐
#pragma pack(2) //以2字节对齐
//#pragma pack(8) //只能缩小不能放大,也就是说通过这种方法,不能使程序按照大于4字节的空间对齐,只能小于或等于4字节的空间对齐
方法二:在定义结构体的时候如下定义:
struct ss{
int a;
char b;
int c;
char d;
}__attribute__((packed)); //取消字节对齐
//以8字节对齐 该种方法只能放大不能缩小 ,也就是说通过这种方法,不能使程序按照小于4字节的空间对齐,只能大于4字节的空间对齐
//__attribute__((aligned(8)));
Wechat:Shalomlin QQ:373381362