标签:改变 字节对齐 时间 pop 文件 偏移量 大数 int 否则
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
平台原因:
某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
性能原因:
为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总的来说,内存对齐就是拿空间换取时间的做法,目的是为了让CPU能一次获取到数据,从而提升性能。
该预处理指令用来改变对齐参数。在缺省情况下,C编译器为每一个变量或数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对齐参数:
· 使用伪指令#pragma pack (n),C编译器将按照n字节对齐。
· 使用伪指令#pragma pack (),取消自定义字节对齐方式。
也可以写成:
#pragma pack(push,n)
#pragma pack(pop)
#pragma pack (n)表示每个成员的对齐单元不大于n(n为2的整数次幂)。这里规定的是上界,只影响对齐单元大于n的成员,对于对齐字节不大于n的成员没有影响。
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
规则:
1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
3、结合1、2颗推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。
#include <stdio.h> #include <stdint.h> #pragma pack(1) typedef struct { /*成员对齐*/ int a; //长度4 < 1 **按1对齐**;偏移量为0;存放位置区间[0,3] char b; //长度1 = 1 **按1对齐**;偏移量为4;存放位置区间[4] short c; //长度2 > 1 **按1对齐**;偏移量为5;存放位置区间[5,6] char d; //长度1 = 1 **按1对齐**;偏移量为6;存放位置区间[7] /*整体对齐*/ //整体对齐系数 = min(对齐系数1,最大成员长度4) = 1,无需对齐,整体大小为8; }test_pack1; #pragma pack() #pragma pack(2) typedef struct {
/*成员对齐*/ int a; //长度4 > 2 **按2对齐**;偏移量为0;存放位置区间[0,3] char b; //长度1 < 2 **按1对齐**;偏移量为4;存放位置区间[4] short c; //长度2 = 2 **按2对齐**;偏移量要提升到2的倍数6;存放位置区间[6,7] char d; //长度1 < 2 **按1对齐**;偏移量为7;存放位置区间[8];共九个字节 /*整体对齐*/ //整体对齐系数 = min(对齐系数2,最大成员长度4) = 2,将9提升到2的倍数10,整体大小为10; }test_pack2; #pragma pack() #pragma pack(4) typedef struct {
/*成员对齐*/ int a; //长度4 = 4 **按4对齐**;偏移量为0;存放位置区间[0,3] char b; //长度1 < 4 **按1对齐**;偏移量为4;存放位置区间[4] short c; //长度2 < 4 **按2对齐**;偏移量要提升到2的倍数6;存放位置区间[6,7] char d; //长度1 < 4 **按1对齐**;偏移量为7;存放位置区间[8];总大小为9 /*整体对齐*/ //整体对齐系数 = min(对齐系数4,最大成员长度4) = 4,将9提升到4的倍数12,整体大小为12; }test_pack4; #pragma pack() #pragma pack(8) typedef struct {
/*成员对齐*/ int a; //长度4 < 8 **按4对齐**;偏移量为0;存放位置区间[0,3] char b; //长度1 < 8 **按1对齐**;偏移量为4;存放位置区间[4] short c; //长度2 < 8 **按2对齐**;偏移量要提升到2的倍数6;存放位置区间[6,7] char d; //长度1 < 8 **按1对齐**;偏移量为7;存放位置区间[8],总大小为9 /*整体对齐*/ //整体对齐系数 = min(对齐系数8,最大成员长度4) = 4,将9提升到4的倍数12,整体大小为12; }test_pack8; #pragma pack() test_pack1 pack1 = {0x11111111, 0x22, 0x3333, 0x44}; uint8_t *ptrPack1 = (uint8_t *)&pack1; test_pack2 pack2 = {0x11111111, 0x22, 0x3333, 0x44}; uint8_t *ptrPack2 = (uint8_t *)&pack2; test_pack4 pack4 = {0x11111111, 0x22, 0x3333, 0x44}; uint8_t *ptrPack4 = (uint8_t *)&pack4; test_pack8 pack8 = {0x11111111, 0x22, 0x3333, 0x44}; uint8_t *ptrPack8 = (uint8_t *)&pack8; int main(int argc, char *argv[]) { int i = 0; printf("#pragma pack(1) \tsize:%2d,\t", sizeof(pack1)); for(i=0; i<sizeof(pack1); i++) { printf("%02x", ptrPack1[i]); } printf("\r\n"); printf("#pragma pack(2) \tsize:%2d,\t", sizeof(pack2)); for(i=0; i<sizeof(pack2); i++) { printf("%02x", ptrPack2[i]); } printf("\r\n"); printf("#pragma pack(4) \tsize:%2d,\t", sizeof(pack4)); for(i=0; i<sizeof(pack4); i++) { printf("%02x", ptrPack4[i]); } printf("\r\n"); printf("#pragma pack(8) \tsize:%d,\t", sizeof(pack8)); for(i=0; i<sizeof(pack8); i++) { printf("%02x", ptrPack8[i]); } printf("\r\n");
return 0; }
该段测试实例测试同一结构体,为了观察方便,成员的每一个字节值按顺序分别为十六进制0xaa、0xbb、0xcc、0xdd。
分别进行1、2、4、8字节对齐方式对齐,计算其占用内存大小及在内存的排列的结果,具体对齐步骤见代码注释部分。
一般情况下都不需要对编译器进行的内存对齐规则进行修改,因为这样会降低程序的性能,只有这个结构在涉及到对外交互的情况下,比如这个结构需要对外协议交互、写入文件等。
标签:改变 字节对齐 时间 pop 文件 偏移量 大数 int 否则
原文地址:https://www.cnblogs.com/damaohai/p/11606950.html