C语言结构体内存对齐

结构体内存对齐

如何计算结构体的大小?

首先得掌握结构体的对齐规则:

1.第一个成员在与结构体变量偏移量为0的地址处。(将第一个成员放在结构体内存的第0处)

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。(从0地址处开始,偏移量逐渐增减,每个字节加一,第二个成员开始,将成员的字节大小跟对齐数(VS2019编译器的默认对齐数是 8)进行比较,选两者中较小的,把该变量放到小的对齐数的偏移量地址上)

对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。

  • VS中默认的对齐数为8
  • Linux - 没有默认对齐数的概念

3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。(如果全部成员自身的的大小已经放入内存,但最后一个成员的尾字节所在的偏移量不是最大对其数的整数倍,则在最后要浪费空间直到刚好到整数倍为止)

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

struct S
{				//类型大小		VS默认对齐数			两者中小的对齐数
	char c1;	 //  1				8					1
	int i;		 //	 4			    8					4
	double d;	 //  8				8					8
};
int main()
{
    struct S s;
    printf("%d\n",sizeof(s));	//结构体大小为16,并不是简单的变量大小相加
}

C语言结构体内存对齐_第1张图片

为什么存在内存对齐?

大部分的参考资料都是这样说的:

1.平台原因(移植原因)︰不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

⒉.性能原因︰数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

总体来说︰

结构体的内存对齐是拿空间来换取时间的做法。


那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到∶

1.让占用空间小的成员尽量集中在一起。

2.修改默认对齐数

之前我们见过了#pragma这个预处理指令,这里我们再次使用,可以改变我们的默认对齐数。

#pragma pack(2)			//设置默认对齐数为2
struct s1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()			//取消设置的默认对齐数,还原为默认

此时结构体大小为8,如果设置默认对齐数为1的话,则成员紧挨着存放,等于没有对齐


offsetof宏:

用于计算结构体中某变量相对于首地址的偏移量(以字节为单位)

函数原型:offsetof (type,member)

使用:

#include 
#include 		//需要引入头文件
struct s1
{
	char c1;
	int i;
	char c2;
};
int main()
{
   	printf("%d\n", offsetof(struct s1, c1));	//0,C1在偏移量为0的地址处
	printf("%d\n", offsetof(struct s1, i));		//4,i在偏移量为4的地址处
	printf("%d\n", offsetof(struct s1, c2));	//8,C2在偏移量为8的地址处
    return 0;
}        

模拟实现offsetof宏:

#include 
struct A
{
	char a;
	int b;
	char c;
    double d;
};
#define OFFSETOF(struct_name,mem_name) (int)&(((struct_name *)0)->mem_name)
int main()
{
	//模拟实现宏offsetof
	printf("%d\n", OFFSETOF(struct A, a));
	printf("%d\n", OFFSETOF(struct A, b));
	printf("%d\n", OFFSETOF(struct A, c));
	printf("%d\n", OFFSETOF(struct A, d));
	return 0;
}

解释:定义宏时,将0强制转换为结构体类型,只是0不再是个整型,而是个地址,是结构体的首地址,并没有真正去创建结构体变量,然后通过->找到相应的结构体变量,&取出该变量的地址,减去0地址,再强制类型转换为int型,就能输出变量相对于首地址的偏移量,这里没有减-0,因为没有意义,如果规定首地址是0x10,那最后强转为int之前就要减去首地址0x10,才是偏移量

你可能感兴趣的:(C语言,c语言)