806-如何通过编译器选项查看 C++ 类内存布局

使用 CL 编译器选项查看 C++ 类内存布局

今天,在这里和大家分享一下, VS系列编译器 CL 的一个编译选项可以查看 C++ 类的内存布局,非常有用。
使用如下:
打开VS2019编译器:
在这里插入图片描述
选择 VS 的命令行工具,按如下格式使用:

>cl –d1reportSingleClassLayout[classname]  test.cpp

而使用 –d1reportAllClassLayout 则可以查看源文件中所有类及结构体的内存布局。

其中,classname 为类名,我们可以自由选择想要查看的类名-d1reportSingleClassLayout[classname] 之间没有空格。
编写程序测试:
比较奇怪,加上 #include < iostream > 后,测试结构体的时候就会出现很输出,应该是库中的类,看起来真麻烦,所以这里去掉它。

//#include <iostream>

//using namespace std;

struct S
{
	char x;
	int y;
	double z;
};
class TestClass
{
private:
	char y;
	double z;
	int x;
};
//base
class Base
{
private:
	int x;
public:
	virtual void f1();
	virtual int g1();
};
//Derived
class Derived : public Base
{
private:
	char y;
public:
	virtual float f2();
};
//Derived2
class Derived2 : public Base
{
private:
	double z;
public:
	virtual void f1();
	virtual float v2();
	int f3();
};
//
class Base2
{
private:
	int yy;
public:
	virtual void g2();
};
//多重继承
class Derived3 : public Base, public Base2
{
private:
	double zz;
public:
	virtual void g3();
};
//
int main()
{
	return 0;
}

测试1:测试结构体 S:>cl Test.cpp /d1reportSingleClassLayoutS
在这里插入图片描述
可以看到,VS 默认情况下,结构体内使用字节对齐,char x, 和 int y 之间填充了 3 个字节的空间。默认情况,VS对结构体内的字节按最大字节对齐,成员变量之间的顺序不同,结构体所占空间也可能不同。

测试2: 测度类 TestClass: >cl Test.cpp /d1reportSingleClassLayoutTestClass
在这里插入图片描述
同样可以看到,类 TestClass 中数据成员的按最大数据成员字节对齐,char y 和 double z 之间插入了 7 个字节,double z 和 int x 之间插入了 4 个字节,按 double 型对齐,32 位机器上, sizeof(double) = 8。

测试3:测试有虚函数的类 Base: >cl Test.cpp /d1reportSingleClassLayoutBase
在这里插入图片描述
其中{vfptr}是虚函数表,可以看到,VC 将虚函数表地址放在了对象的头 4 个字节,接着才是数据成员。虚函数表是一个数组,里面存放的是类中虚函数的地址,可以看到虚函数成员的地址是按照声明的顺序存放的。

测试4:测试子类 Derived:>cl Test.cpp /d1reportSingleClassLayoutDerived
在这里插入图片描述
在这里插入图片描述
可以看到,基类的虚函数存放在虚表的前面,子类中自己声明的虚函数按顺序存放在后面。

测试5:测试子类Derived2: >cl Test.cpp /d1reportSingleClassLayoutDerived2
在这里插入图片描述
可以看到,子类 Derived2 中重写了基类 Base 中的虚函数 f1(),因此 Devried2 的虚表中 f1() 的位置被 Derived2 重写的 f1() 代替,因此便实现了多态。非虚函数地址不存放在虚表中。

测试6:测试多重继承的类Derived3: >cl Test.cpp /d1reportSingleClassLayoutDerived3
在这里插入图片描述

可以看到VS中对多重继承的处理,子类 Derived3 的对象中,前 4 个字节存放的是第一个基类的 虚表,然后是第一个基类的数据成员。接着是第 2 个基类的虚表及数据成员。最后才是自己的数据成员。其中,Derived3::$vftable@Base2@: -8, -8 表示第 2 个基类相对于虚表相对于 Derived3 的偏移量 offset。

测试结构体的字节对齐

测试结构体的字节对齐,以及 #pragma pack(1), offsetof(struct,number) 的用法

#include <iostream>

using namespace std;

struct st1
{
	short number;
	float grade;
	float grade2;
	float grade3;
	char level;
}; //20

struct st2
{
	char level;
	short number;
	float grade;
	float grade2;
	float grade3;
};//16

#pragma pack(1)
struct st3
{
	char level;
	short number;
	float grade;
	float grade2;
	float grade3;
}; //15
#pragma pack()

void TestSizeOf()
{
	cout << __FUNCTION__ << endl;

	cout << " sizeof(short)= " << sizeof(short) << endl << endl;

	cout << " sizeof(st1)= " << sizeof(st1) << endl;
	cout << " offsetof(st1,number) " << offsetof(st1, number) << endl;
	cout << " offsetof(st1,grade) " << offsetof(st1, grade) << endl;
	cout << " offsetof(st1,grade2) " << offsetof(st1, grade2) << endl;
	cout << " offsetof(st1,grade3) " << offsetof(st1, grade3) << endl;
	cout << " offsetof(st1,level) " << offsetof(st1, level) << endl << endl;

	cout << " sizeof(st2)= " << sizeof(st2) << endl;
	cout << " offsetof(st2,level) " << offsetof(st2, level) << endl;
	cout << " offsetof(st2,number) " << offsetof(st2, number) << endl;
	cout << " offsetof(st2,grade) " << offsetof(st2, grade) << endl;
	cout << " offsetof(st2,grade2) " << offsetof(st2, grade2) << endl;
	cout << " offsetof(st2,grade3) " << offsetof(st2, grade3) << endl << endl;

	cout << " sizeof(st3)= " << sizeof(st3) << endl;
	cout << " offsetof(st3,level) " << offsetof(st3, level) << endl;
	cout << " offsetof(st3,number) " << offsetof(st3, number) << endl;
	cout << " offsetof(st3,grade) " << offsetof(st3, grade) << endl;
	cout << " offsetof(st3,grade2) " << offsetof(st3, grade2) << endl;
	cout << " offsetof(st3,grade3) " << offsetof(st3, grade3) << endl << endl;
}
int main()
{
	TestSizeOf();
	return 0;
}

其中,VS对结构体中的数据成员默认按照最大成员对齐,#pragma pack(num) 可以设置对齐字节数,可以为1、2、4、8、16 。也可以使用编译选项 /Zp[1|2|4|8|16] 修改对齐方式,取消修改用#pragma pack(),如果结构体某成员的 sizeof 大于你设置的,则按你的设置来对齐。注意 offsetof 的用法,可以很容易观察结构体的内部结构

还可以使用前面所说的 cl –d1reportSingleClassLayout[classname] test.cpp 编译选项进行相互验证。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林林林ZEYU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值