结构体的内存对齐
一、为什么要内存对齐
为了高速处理数据,现代处理器的设计引入了对齐的概念。所谓对齐就是保证数据在内存中存储时地址变化按照一定的规律,这样就可以保证每次cpu取同样长度的数据进行运算,因此可以提高计算机的运行速度。
二、什么是内存对齐
下面的程序演示内存中的对齐:
#include <stdio.h>
struct test{
char ch;
short s;
int i;
};
int main(void)
{
struct test var;
printf("size of var : %d\n", sizeof(var));
return 0;
}
运行结果为:
该结构体拥有一个char型、一个short型和一个int型成员变量,所占用的字节数应该是7个,但是结果却是8个字节,这是因为编译器采用了默认的对齐方式。
三、内存对齐的一些概念
1.数据类型自身的对齐值
基本数据类型的自身对齐值为其数据类型的大小。
2.指定对齐值
默认的指定对齐值为成员中自身对齐值最大的那个值。
使用下面语句可以指定对齐值value:
#progma pack (value) /*指定按value字节对齐*/
struct A {
int a;
};
#progma pack () /*取消指定对齐,恢复缺省对齐*/
3.结构体或者类的自身对齐值
其成员中自身对齐值最大的那个值。
4.数据类型、结构体和类的有效对齐值
自身对齐值和指定对齐值中较小的那个值。
四、内存对齐的实现
设结构体如下:
struct test{
char ch;
int i;
short s;
};
假设test的起始地址为0x0000,默认的指定对齐值为4,所以:
- 第一个成员变量ch,自身对齐值为1,小于指定对齐值4,所以有效对齐值为1,ch的存放地址为0x0000,并且0x0000是自身对齐值1的倍数。
- 第二个成员变量i,自身对齐值是4,等于指定对齐值4,所以有效对齐值为4,而变量起始地址应为自身有效对齐值的倍数,所以i应存放在0x0004到0x0007这四个连续的字节空间。
- 第三个成员变量s,自身对齐值为2,小于指定对齐值为4,所以有效对齐值为2,变量起始存放地址应为2的倍数,所以s存放在0x0008到0x0009两个字节空间。
- 接下来看结构体test自身对齐值为其变量中最大对齐值(这里是i)所以就是4,所以结构体的有效对齐值也是4,结构体的大小应为结构体有效对齐值的倍数,所以所以0x0000A到0x000B也被结构体所占用,0x0000到0x000B共12个字节,这就是结构体test所占内存大小。
当我们指定对齐值为2字节时,再分析上面的结构体
#pragma pack(2) //指定按2字节对齐
struct test1{
char ch;
int i;
short s;
};
#pragma pack() //取消指定对齐,恢复缺省对齐
假设test1的起始地址为0x0000,指定对齐值为2,所以:
- 第一个成员变量ch,自身对齐值为1,小于指定对齐值为2,所以有效对齐值为1,ch存放在0x0000。
- 第二个成员变量i,自身对齐值为4,大于指定对齐值,所以有效对齐值为2,存放的首地址为有效对齐值2的倍数即0x0002,因此i存放在0x0002,0x0003,0x0004,0x0005四个连续的字节空间。
- 第三个成员变量s,自身对齐值为2,等于指定对齐值,所以有效对齐值为2,存放首地址为2的倍数即0x0006,因此s存放在0x0006,0x0007两个字节空间。
- 结构体test1自身对齐值即最大成员变量自身对齐值4,大于指定对齐值2,所以有效对齐值为2,而结构体大小为0x0000到0x0007共8个字节,是2的倍数,因此结构体test1的内存大小为8字节
五、分析几个程序
1.程序如下:
#include <stdio.h>
#pragma pack(8)
struct example1
{
short a;
long b;
};
struct example2
{
char c;
struct example1 struct1;
short e;
};
#pragma pack()
int main(int argc, char* argv[])
{
struct example2 struct2;
struct example1 e1;
printf("size of example1 : %d\n", sizeof(e1));
printf("size of example2 : %d\n", sizeof(struct2));
printf("%d\n", (unsigned long int)(&struct2.struct1) - (unsigned long int)(&struct2));
return 0;
}
程序的输出结果是什么?
答案是:
分析这个程序:
对于example1:
- 成员变量a,自身对齐值为2,指定对齐值为8,所以有效对齐值为2,因此a占2个字节空间。
- 成员变量b,自身对齐值为8,等于指定对齐值,所以有效对齐值为8,起始地址应为8的倍数,因此需要填充6个字节,b占8个字节空间。
- 结构体的有效对齐值为8,而成员变量共占16个字节,是8的倍数,因此结构体example1共占16个字节空间。
对于example2:
- 成员变量c,自身对齐值为1,小于指定对齐值,所以有效对齐值为1,占1个字节空间。
- 成员struct1是一个example1结构体,该结构体的自身对齐值为8,等于指定对齐值,所以起始地址为8的倍数,因此填充7个字节,占16个字节空间。
- 成员e,自身对齐值2,小于指定对齐值,有效对齐值为2,占两个字节大小。
- 结构体多有成员所占大小为:1+7+16+2=26,而结构体的有效对齐值为8,因此结构体需占32个字节空间(8的倍数)。
对于(unsigned long int)(&struct2.struct1) - (unsigned long int)(&struct2):
因为在example2中struct1之前有个char类型的成员变量,由于struct1的对齐值为8所以char变量需要填充7个字节,因此结果为8。
2.第二个程序:
#include <stdio.h>\
union A
{
int a[5];
char b;
double c;
};
struct B
{
int n;
union A a;
char c[10];
};
int main(void)
{
union A a1;
struct B b;
printf("size of union : %d\n", sizeof(a1));
printf("size of struct : %d\n", sizeof(b));
return 0;
}
A和B的大小为:
分析程序:
由于union是共享内存的,首先看每个成员所占大小:
union A
{
int a[5]; //20字节
char b; //1字节
double c; //8字节
};
A中各变量默认内存对齐方式,必须以最长的double 8字节对齐,所以A的大小为24字节(20+4)。
结构体B中:
struct B
{
int n; //4字节
union A a; //24字节
char c[10]; //10字节
};
由于A是8字节对齐的,所以B中的int与char[]也需要8字节对齐,所以大小为:8+24+16 = 48。
转载于:https://my.oschina.net/daowuming/blog/769781
上一篇: auto && decltype