使用 CL 编译器选项查看 C++ 类内存布局--转
今天查资料的时候发现 VS 编译器 CL 的一个编译选项可以查看 C++ 类的内存布局,非常有用。使用如下,从开始程序菜单找到 Visual Stdio 2008/2005 Command Prompt,选择 VS 的命令行工具,按如下格式使用:
>cl –d1reportSingleClassLayout[classname] test.cpp
而使用 –d1reportAllClassLayout 则可以查看源文件中所有类及结构体的内存布局。
其中,classname 为类名,-d1reportSingleClassLayout[classname] 之间没有空格。编写程序测试:
比较奇怪,加上 #include <iostream> 后,测试结构体的时候就会出现很输出,应该是库中的类,看起来真麻烦,所以这里去掉它。
11: //test: >cl Test.cpp /d1reportSingleClassLayout[className]
12: //#include <iostream>
13:
14: //using namespace std;
15:
16: struct S
17: {
18: char x;
19: int y;
110: double z;
111: };
112: class TestClass
113: {
114: private:
115: char y;
116: double z;
117: int x;
118: };
119: //base
120: class Base
121: {
122: private:
123: int x;
124: public:
125: virtual void f1();
126: virtual int g1();
127: };
128: //Derived
129: class Derived: public Base
130: {
131: private:
132: char y;
133: public:
134: virtual float f2();
135: };
136: //Derived2
137: class Derived2: public Base
138: {
139: private:
140: double z;
141: public:
142: virtual void f1();
143: virtual float v2();
144: int f3();
145: };
146: //
147: class Base2
148: {
149: private:
150: int yy;
151: public:
152: virtual void g2();
153: };
154: //多重继承
155: class Derived3: public Base, public Base2
156: {
157: private:
158: double zz;
159: public:
160: virtual void g3();
161: };
162: //
163: int main()
164: {
165: return 0;
166: }
//测试,1:测试结构体 S:>cl Test.cpp /d1reportSingleClassLayoutS
可以看到,VC 默认情况下,结构体内使用字节对齐,char x, 和 int y 之间填充了 3 个字节的空间。默认情况,VC 对结构体内的字节按最大字节对齐,成员变量之间的顺序不同,结构体所占空间也可能不同。
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
可以看到VC中对多重继承的处理,子类 Derived3 的对象中,前 4 个字节存放的是第一个基类的 虚表,然后是第一个基类的数据成员。接着是第 2 个基类的虚表及数据成员。最后才是自己的数据成员。其中,Derived3::$vftable@Base2@: -8, -8 表示第 2 个基类相对于虚表相对于 Derived3 的偏移量 offset。
//测试结构体的字节对齐,以及 #pragma pack(1), offsetof(struct,number) 的用法。
11: #include <iostream>
12:
13: using namespace std;
14:
15: struct st1
16: {
17: short number;
18: float grade;
19: float grade2;
110: float grade3;
111: char level;
112: }; //20
113:
114: struct st2
115: {
116: char level;
117: short number;
118: float grade;
119: float grade2;
120: float grade3;
121: };//16
122:
123: #pragma pack(1)
124: struct st3
125: {
126: char level;
127: short number;
128: float grade;
129: float grade2;
130: float grade3;
131: }; //15
132: #pragma pack()
133:
134: void TestSizeOf()
135: {
136: cout << __FUNCTION__ << endl;
137:
138: cout << " sizeof(short)= " << sizeof(short) << endl << endl;
139:
140: cout << " sizeof(st1)= " << sizeof (st1) << endl;
141: cout << " offsetof(st1,number) " << offsetof(st1,number) << endl;
142: cout << " offsetof(st1,grade) " << offsetof(st1,grade) << endl;
143: cout << " offsetof(st1,grade2) " << offsetof(st1,grade2) << endl;
144: cout << " offsetof(st1,grade3) " << offsetof(st1,grade3) << endl;
145: cout << " offsetof(st1,level) " << offsetof(st1,level) << endl << endl;
146:
147: cout << " sizeof(st2)= " << sizeof (st2) << endl;
148: cout << " offsetof(st2,level) " << offsetof(st2,level) << endl;
149: cout << " offsetof(st2,number) " << offsetof(st2,number) << endl;
150: cout << " offsetof(st2,grade) " << offsetof(st2,grade) << endl;
151: cout << " offsetof(st2,grade2) " << offsetof(st2,grade2) << endl;
152: cout << " offsetof(st2,grade3) " << offsetof(st2,grade3) << endl << endl;
153:
154: cout << " sizeof(st3)= " << sizeof (st3) << endl;
155: cout << " offsetof(st3,level) " << offsetof(st3,level) << endl;
156: cout << " offsetof(st3,number) " << offsetof(st3,number) << endl;
157: cout << " offsetof(st3,grade) " << offsetof(st3,grade) << endl;
158: cout << " offsetof(st3,grade2) " << offsetof(st3,grade2) << endl;
159: cout << " offsetof(st3,grade3) " << offsetof(st3,grade3) << endl << endl;
160: }
161: int main()
162: {
163: TestSizeOf();
164: return 0;
165: }
其中,VC对结构体中的数据成员默认按照最大成员对齐,#pragma pack(num) 可以设置对齐字节数,可以为1、2、4、8、16 。也可以使用编译选项 /Zp[1|2|4|8|16] 修改对齐方式,取消修改用#pragma pack(),如果结构体某成员的 sizeof 大于你设置的,则按你的设置来对齐。注意 offsetof 的用法,可以很容易观察结构体的内部结构
还可以使用前面所说的 cl –d1reportSingleClassLayout[classname] test.cpp 编译选项进行相互验证。
最后
以上就是俭朴大侠最近收集整理的关于使用 CL 编译器选项查看 C++ 类内存布局--转的全部内容,更多相关使用内容请搜索靠谱客的其他文章。
发表评论 取消回复