前言:
为了方便查看博客,特意申请了一个公众号,附上二维码,有兴趣的朋友可以关注,和我一起讨论学习,一起享受技术,一起成长。
1. #pragma pack
测试:
#include <stdio.h>
int main(void)
{
struct tagTestStruct_T
{
char ch; //1字节
short sh; //2字节
char str; //1字节
int i; //4字节
};
struct tagTestStruct_T st_TestStruct;
printf("ch %p,sh %p,str %p,i %p \r\n",
(unsigned int)(void *)&st_TestStruct.ch - (unsigned int)(void *)&st_TestStruct,
(unsigned int)(void *)&st_TestStruct.sh - (unsigned int)(void *)&st_TestStruct,
(unsigned int)(void *)&st_TestStruct.str - (unsigned int)(void *)&st_TestStruct,
(unsigned int)(void *)&st_TestStruct.i - (unsigned int)(void *)&st_TestStruct);
system("pause");
return 0;
}
输出:
2. 内存对齐
字(2字节),双字(4字节),和四字(8字节)在自然边界上不需要在内存中对齐。(对字,双字,和四字来说,自然边界分别是偶数地址,可以被 4 整除的地址,和可以被 8 整除的地址。)无论如何,为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于:为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。
了提高 CPU 的存储速度,编译器对一些变量的起始地址做了“对齐”处理。在默认情况下,编译器规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式:
类型 | 对齐方式 |
---|---|
char | 偏移量必须为sizeof(char)即1的倍数 |
int | 偏移量必须为sizeof(int)即4的倍数 |
float | 偏移量必须为sizeof(float)即4的倍数 |
double | 偏移量必须为sizeof(double)即8的倍数 |
short | 偏移量必须为sizeof(short)即2的倍数 |
各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节编译器会自动填充。同时编译器为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。
注:
对齐方式:变量存放的起始地址相对于结构的起始地址的偏移量;
3. 对齐规则
每个平台都有自己默认的“对齐系数”,在 VS2013 上测试为 4 字节对齐。我们可以通过命令 #pragma pack(n) 对内存进行对齐。通过预编译命令#pragma pack(n),n = 1,2,4,8,16 来改变对齐系数。
有效对齐值:是 #pragma pack(n) 指定的长度 n 与结构体中的成员变量中比较小的。
eg:
#pragma pack(1) 指定一字节对齐,而结构体中也定义有一字节的变量,那么对齐单位就是一字节;同样地,如果 #pragma pack(2) 指定 2 字节对齐,而结构体中也定义没有一字节的变量,那么对齐单位就是 2 字节,若结构体中有一字节变量,还是以一字节对齐;
如下的测试:
#include <stdio.h>
#pragma pack(2)
int main(void)
{
struct tagTestStruct_T
{
short sh;
int i;
};
struct tagTestStruct_T st_TestStruct;
printf("size is %d \r\n",sizeof(st_TestStruct));
system("pause");
return 0;
}
输出结果为 2 字节对齐:
注:
使用指令 #pragma pack (n),设置编译器将按照 n 个字节对齐。
使用指令 #pragma pack (),编译器将取消自定义字节对齐方式。
4. 规则测试
定义如下结构体:
#include <stdio.h>
//#pragma pack(1)
int main(void)
{
struct tagTestStruct_T
{
char ch;
short sh;
char str;
int i;
};
struct tagTestStruct_T st_TestStruct;
printf("size is %d \r\n",sizeof(st_TestStruct));
system("pause");
return 0;
}
4.1 采用默认对齐
测试程序先注释掉://#pragma pack(1)。输出结果为:
内存边界从 4 的倍数开始,如下图:
4.2 设置 1 字节对齐
测试程序先注释掉:打开 #pragma pack(1)。输出结果为:
内存边界从 1 开始,如下图:
参考:
1.C/C++内存对齐详解
2.C语言内存对齐详解