什么是内存对齐
还是用一个例子带出这个问题,看下面的小程序,理论上,32位系统下,int占4byte,char占一个byte,那么将它们放到一个结构体中应该占4+1=5byte;但是实际上,通过运行程序得到的结果是8 byte,这就是内存对齐所导致的。
#include<stdio.h>
struct{
int x;
char y;
}s;
int main()
{
printf("%d\n",sizeof(int)); // 4
printf("%d\n",sizeof(char)); // 1
printf("%d\n",sizeof(s)); // 输出8
return 0;
}
现代计算机中内存空间都是按照 byte 划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但是实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐。
为什么要进行内存对齐
1.平台原因(移植原因): 一些资料上是这样说的,“不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些特定地址处取某些特定的数据,否则就会抛出硬件异常”。也就是说在计算机在内存读取数据时,只能在规定的地址处读数据,而不是内存中任意地址都是可以读取的。
2.效率原因: 正是由于只能在特定的地址处读取数据,所以在访问一些数据时,对于访问未对齐的内存,处理器需要进行两次访问;而对于对齐的内存,只需要访问一次就可以。 其实这是一种以空间换时间的做法,但这种做法是值得的。
结构体默认对齐
规则
- 对于结构体中的各个成员,第⼀个成员位于偏移为 0 的位置,以后的每个数据成员的偏移ᰁ必须是min(#pragma pack() 制定的数,数据成员本身⻓度) 的倍数。
- 在所有的数据成员完成各⾃对⻬之后,结构体或联合体本身也要进⾏对⻬,整体⻓度是 min(#pragma pack()制定的数,⻓度最⻓的数据成员的⻓度) 的倍数。
- 64位操作系统上64位编译器:默认8字节对齐
- 64位操作系统上32位编译器:默认8字节对齐
- 32位操作系统上32位编译器:默认4字节对齐
以下两点是对于64位操作系统上64位编译器:默认8字节对齐而言的,如果是4字节对齐,只需要将8改成4即可:
- 结构体整体本身安置在8字节对齐处,结构体对齐后的大小必须是8的倍数
- 结构体中每个元素占的字节大小是自身对齐参数的整数倍
以下是32/64位Linux上GCC编译环境下各类型变量的自身对齐参数
编译器考虑结构体存放时,以满足以上2点要求的最少内存需要来存放。
分析
1、
typedef struct struct_test1
{
int a; // 4
double b; // 8
char c; // 1
}ST1;
int main(void)
{
printf("sizeof(ST1) = %d\n", (int)sizeof(ST1)); // 24
char *p = NULL;//根据指针长度来辨别编译器和操作系统位数
printf("sizeof(p) = %d\n", (int)sizeof(p)); //8
return 0;
}
64位操作系统上默认是8字节对齐,整体所占字节数分析如下:
- 首先整个结构体本身安置在8字节对齐处,这个是由编译器保证的。
- 然后是第一个元素a,a的开始地址就是整个结构体的开始地址,所以自然是8字节对齐的。但是a的结束地址要由下一个元素具体决定
- 之后是第二个元素b,b为double型的,自身对齐长度是8,所以不能自接放在a的后面,也就是说a需要另外填充4个字节空间再结束,然后放b,由于b本身就是对齐的,所以无需另外填充,直接结束
- 直接放c,c放完之后也不能自接结束,因为要保证整个结构体大小是8的整数倍,所以c后面需填充7个字节的内存空间。
所以sizeof(ST1) = (4 + 4) + 8 + (1 + 7) = 24;
#include <stdio.h>
typedef struct mystruct1
{
int a;
char b;
double c;
}MS1;//16
typedef struct mystruct2
{
char a;
char b[15];
char c;
char *p;
}MS2;//(1+15)+(1+7)+8 = 32
typedef struct mystruct3
{
char a;
char b[17];
char c;
char *p;
}MS3;//(1+17+1+5)+8 =32
typedef struct mystruct4
{
struct mystruct1 mys1;
short b[5];
}MS4;//32
int main(void)
{
printf("sizeof(MS1) = %ld\n", sizeof(MS1));
printf("sizeof(MS2) = %ld\n", sizeof(MS2));
printf("sizeof(MS3) = %ld\n", sizeof(MS3));
printf("sizeof(MS4) = %ld\n", sizeof(MS4));
return 0;
}