05 | C语言结构体大小和偏移量


对齐模数的查看和修改

#pragma pack(show)

我们加入这样一条预编译指令,进行编译后即可查看当前的对齐模数

warning C4810: 杂注 pack(show) 的值 == 8

#pragma pack(4)

我们也可以执行这样的一条指令来修改当前的对齐模数

结构体内存对齐规则

  1. 从第一个属性开始,从0开始偏移
  2. 第二个属性开始,要放到该类型的大小对齐模数比较小的值的整数倍
  3. 所有属性都计算完后,再整体做二次偏移,将整体计算的结果放在结构体最大类型对齐模数比较小的值的整数倍上

我们来看这样一个结构体

typedef struct _STUDENT {
	int		a;
	char	b;
	double	c;
	float	d;
}Student;

推理过程

首先对于a本身占据4个字节,即从0开始到3(这里说的“到3”意思是必定>=3)。

b本身为1个字节,对齐模数为8,则根据上面的规则b的起始地址肯定是可以放在4上面的,因为我们要将b放到1的整数倍上,显然4是1的整数倍。重点来了,同时我们发现前一个类型没有占据4,所以我们可以最终决定b的起始位置在4。那么b的终止地址呢?我们目前只知道是>=5的。

c本身为8个字节,对齐模数为8,则c的起始地址就是可以放在8的整数倍上的,显然0是不可以的,我们将c的起始地址放到8上面。那么此时b的终止位置也就定了,为7。c的终止地址,为>=15.

同理d的起始位置为16,终止位置为19.

typedef struct _STUDENT {
	int		a;  // 0~3
	char	b;  // 4~7
	double	c;  // 8~15
	float	d;	// 16~19
}Student;c

注意还没有结束

我们此时数出来的结构体大小为20字节。

20不是8(此结构体中最大的类型double和对齐模数8之间的较小值)的倍数。

因此20整体放大到24.

结构体嵌套结构体

typedef struct _STUDENT2 {
	char	a;  // 0~7
	Student b;	// 8~31
	double	c;  // 32~39
}Student2;

我们对于这种该怎么办呢?

补充一点。我们是知道Student的大小的,为24,那它的起始位置呢?

对于结构体嵌套结构体我们将子结构体起始位置放到 子结构体中最大类型(此例子为double)和对齐模数较小值的整数倍上。即我们得到起始位置为8.

Student2不需要二次对齐(我们将子结构体看作子结构体中最大类型),则最终答案为40.

利用偏移量操作结构体

话不多说直接上代码

struct Teacher
{
	char a; //0~3
	int b;	//4~7
};
struct Teacher2
{
	char a;
	int b;
	struct Teacher c; //看待该结构体直接展开就行
};
void test01() {  //计算偏移量
	struct Teacher t1;
	struct Teacher *p = &t1;

	printf("b的属性偏移量为:%d\n", (int)&(p->b) - (int)p);
	printf("b的属性偏移量为:%d\n", offsetof(struct Teacher, b));
}
void test02() { //通过偏移量来访问内存
	struct Teacher2 t1 = { 'a',10,'b',20 };

	int offset1 = offsetof(struct Teacher2, c);
	int offset2 = offsetof(struct Teacher, b);

	printf("%d\n", *(int*)((char*)&t1 + offset1 + offset2));
	printf("%d\n", ((struct Teacher*)((char*)&t1 + offset1))->b);

}

需要添加一个头文件

#include

其中包含了offsetof这个宏!