2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > C语言 结构体 内存对齐 对齐参数 偏移量

C语言 结构体 内存对齐 对齐参数 偏移量

时间:2024-04-22 21:08:33

相关推荐

C语言 结构体 内存对齐 对齐参数 偏移量

文章目录

sizeof()内存对齐内存对齐的背景全部规则汇总(看这里)典型例子:Pragma Pack(n)与内存分配#pragma pack对对齐模数的影响偏移量

sizeof()

对于数据类型的sizeof,取决于CPU位数,编译器,汇编等。

通常来说

32位

指针都是 4个字节char1个字节short 两个字节int4个字节long4个字节long int 4个字节float 4个字节double 8个字节long double 8个字节

64位

指针都是一个字长, 8个字节char 1个字节 short 2个字节int4个字节 long 8个字节long int 8个字节double 8个字节long double 也可以变长了, 16个字节

内存对齐

内存对齐的背景

因为计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

例如:

typedef struct mA{int a;char b;short c;}A;

假如我们不使用字节对齐,那么这个结构体就是4+1+2=7个字节,但是我们实际打印确是8个字节。

应用:

但是在实际工程中,如果我们是读取一块一块的数据,这些数据都是连在一起的,比如bmp图片,前14个字节是文件信息头,紧接着是40个字节的图像信息头。如果我们不用结构体对齐操作的话。那就乱了套了,数据就读取失败。 所以我们要在结构体前加上#Pragma Pack(1),以一个字节对齐,使用完后要加#pragma pack(pop),释放内存对齐。为啥要以一个字节对齐呢?是因为一般的数据类型都是大于等于1个字节的。这样的话就会按照数据类型原有的的分配。就不会错位了。

#pragma pack(push)#pragma pack(2)//因为变量最小是short,大小是2字节,所以按2字节对齐也行。最好是1字节对齐。typedef struct tagBITMAPINFOHEADER{unsigned intbiSize;intbiWidth;intbiHeight;unsigned shortbiPlanes;unsigned shortbiBitCount;unsigned intbiCompression;unsigned intbiSizeImage;intbiXPelsPerMeter;intbiYPelsPerMeter;unsigned intbiClrUsed;unsigned intbiClrImportant;} BITMAPINFOHEADER;typedef struct tagBITMAPFILEHEADER {unsigned short bfType;unsigned intbfSize;unsigned short bfReserved1;unsigned short bfReserved2;unsigned intbfOffBits;} BITMAPFILEHEADER;#pragma pack(pop)

全部规则汇总(看这里)

1.结构体内,每个变量按xx字节对齐,实际的体现的效果是总大小是xx的倍数

如果#pragma pack()参与了对对齐模数的设定,则取pack设定的,系统默认的,结构体内参数的最大型,三者中的最小。最终结构体大小要是对齐模数的倍数。

结构体里面static变量,因为静态变量的存放位置与结构体实例的存储地址无关,是单独存放在静态数据区的,因此用siezof计算其大小时没有将静态成员所占的空间计算进来。

静态成员如:

typedef struct mC {static double d;//内存上完全不考虑静态变量short c;}C;

结构体大小为2。

例如:

typedef struct mC {char b;int a;short c;}C;

大小是12。64位系统,对齐数,取8和最大的int型4之间的最小,为4.

typedef struct mC {short c;}C;

大小为2。64位系统,对齐数,取8和最大的shrort型2之间的最小,为2.

如果这里加了#pragma pack(4),取设定的4,系统的8,最大的类型short大小2,三者的最小为2.最终结构体大小是2的倍数

typedef struct mC {double d;char b;int a;short c;}C;//20 24C test;printf("sizeof=%d",sizeof(test));

32位系统是20,64位系统是24.

一般VS工程按默认设置:

(另外,VS配置管理器那配置的win32和x64并没起到调整4,和8的作用,还是按系统来的)

2.具体变量的对齐地址规则。可得到结构体内每个变量的地址,和结构体总大小。

例如:

struct A{int a;char b;double c;char d;};printf("sizeof=%d",sizeof(struct A));

在windows系统32位平台上: 默认按4字节对齐。

int占4个字节

char占1个字节

float占4个字节

double占8个字节

注意是最基础的,地址是连续递增,即:上面的结构体A是按,a,b,c,d的顺序写的,那内存地址也要按a,b,c,d的顺序放,不能跳过变成如a,b,d,c.

int a从0偏移开始,占四个字节,即占用0,1,2,3,现在可用偏移为4偏移,接下来存char b; 由于4是1的倍数,故而,b占用4偏移,接下来可用偏移为5偏移,接下来该存double c; 由于5不是8的倍数,所以向后偏移5,6,7,都不是8的倍数,偏移到8时,8是8的倍数,故而c从8处开始存储,占用8,9,10,11,12,13,14,15偏移,现在可用偏移为16偏移,最后该存char d ;因为16是1的倍数,故d占用16偏移,接下来在整体向后偏移一位,现处于17偏移,min(默认对齐参数,类型最大字节数)=8;因为17不是8的倍数,所以继续向后偏移18…23都不是8的倍数,到24偏移处时,24为8的整数倍,故而,该结构体大小为24个字节。

即:该变量存放的地址(从0,开始),必须是该变量类型大小的倍数。

典型例子:

64位系统下,默认按8字节对齐

typedef struct mC {double d;int a;char b;short c;}C;C mTest;printf("sizeof=%d\n",sizeof(mTest));printf("%p,%p,%p,%p",&mTest.d,&mTest.a,&mTest.b,&mTest.c);

注意,如果调换int 和char的位置,总长度变成24了

typedef struct mC {double d;char b;int a;short c;}C;C mTest;printf("sizeof=%d\n",sizeof(mTest));printf("%p,%p,%p,%p",&mTest.d,&mTest.b,&mTest.a,&mTest.c);

同样画图:

Pragma Pack(n)与内存分配

每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

#pragma pack对对齐模数的影响

1)未指定#pragma pack时,系统默认的对齐模数(32位系统4,64位系统8)。

2)指定#pragma pack 对齐模数时,实际取pack 对齐模数和默认的最小值。

比如32位机:

#pragma pack(8),取8和4的最小值4,对齐.

#pragma pack(2),取2和4的最小值2,对齐.

3)结构体里面static变量,因为静态变量的存放位置与结构体实例的存储地址无关,是单独存放在静态数据区的,因此用siezof计算其大小时没有将静态成员所占的空间计算进来。

#pragma pack (push,2)作用:是指把原来对齐方式设置压栈,并设新的对齐方式设置为2个字节对齐#pragma pack(pop) 作用:恢复对齐状态

#pragma pack(push)#pragma pack(2)#pragma pack(pop) 作用:恢复对齐状态

偏移量

如何知道结构体某个成员相对于结构体起始位置的偏移量?

使用offsetof宏来判断结构体中成员的偏移地址。使用offsetof宏需要包含stddef.h头文件,该宏定义如下:

#define offsetof(type,menber) (size_t)&(((type*)0)->member)

巧妙之处在于将地址0强制转换为type类型的指针,从而定位到member在结构体中偏移位置,编译器认为0是一个有效的地址,从而认为0是type指针的起始地址。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。