GCC对象内存布局(win10)
gdb调试的一些用法:
C:\Users\huangx\Desktop\Test> g++ -std=c++11 -g -o test test.cpp //-g 加入调试信息
C:\Users\huangx\Desktop\Test> gdb test //进入调试
(gdb) set p pretty on //美化print的输出
(gdb) b 47 //break line设置断点
(gdb) r //run 运行程序
(gdb) p d //打印对象d的布局
(gdb) x/nfu <address> //examine 查看内存布局 n是要查看内存单元的个数,f是显示的格式(x十六进制),u是字节个数(g八字节),从<address>开始显示。
1,单、多继承,无虚基类、虚函数
源代码
#include <iostream>
#include <typeinfo>
/*
单继承、多继承
无虚继承、虚函数
*/
class Base{
public:
int vla1 = 1;
};
class Derived1:public Base{
public:
int val2 = 2;
};
class Derived2:public Base {
public:
int val3 = 3;
};
class Derived:public Derived1,public Derived2{
public:
int val = 4;
};
int main(){
Derived1 d1;
Derived d;
return 0;
}
结构
内存布局(一格代表四个字节)
(gdb) p d
$1 = {
<Derived1> = {
<Base> = {
vla1 = 1
},
members of Derived1:
val2 = 2
},
<Derived2> = {
<Base> = {
vla1 = 1
},
members of Derived2:
val3 = 3
},
members of Derived:
val = 4
}
(gdb) p sizeof(d)
$2 = 20
//win是小端对齐,所以看起来2和3在1的前面
(gdb) x/10xg &d
0x61fdf0: 0x0000000200000001 0x0000000300000001
0x61fe00: 0x0000000000000004 0x0000000100000032
0x61fe10: 0x0000000a00000002 0x0000000200000001
0x61fe20: 0x00000000001c1850 0x00000000004013c7
0x61fe30: 0x0000000000000000 0x0000000000000032
2,单继承,有虚基类,无虚函数
源代码
#include <iostream>
#include <typeinfo>
/*
单虚继承,无虚函数
*/
class Base{
public:
int vla1 = 1;
};
class Derived1:virtual public Base{
public:
int val2 = 2;
};
class Derived2:virtual public Derived1 {
public:
int val3 = 3;
};
int main(){
Derived1 d1;
auto vptr_Derived1 = (int*)*(int*)&d1;
/* std::cout << *(vptr_Derived1-6) << std::endl; // Vbase_offset
std::cout << *(vptr_Derived1-4) << std::endl; // top_offset
std::cout << *(vptr_Derived1-2) << std::endl; // ptr type_info */
std::type_info* t1 = (std::type_info *)*(vptr_Derived1-2);
std::cout << t1->name() << std::endl; //Derived
Derived2 d2;
return 0;
}
结构
内存布局
(gdb) p d2
$1 = {
<Derived1> = {
<Base> = {
vla1 = 1
},
members of Derived1:
_vptr.Derived1 = 0x404608 <vtable for Derived2+56>,
val2 = 2
},
members of Derived2:
_vptr.Derived2 = 0x4045f0 <vtable for Derived2+32>,
val3 = 3
}
(gdb) p sizeof(d2)
$2 = 32
(gdb) x/10xg &d2
0x61fde0: 0x00000000004045f0 0x0000000000000003
0x61fdf0: 0x0000000000404608 0x0000000100000002
0x61fe00: 0x00000000004045c8 0x0000000100000002
0x61fe10: 0x00000000004044f0 0x00000000004045c8
0x61fe20: 0x0000000000d61850 0x00000000004013c7
(gdb) x/10xg 0x4045d0
0x4045d0 <_ZTV8Derived2>: 0x000000000000001c 0x0000000000000010
0x4045e0 <_ZTV8Derived2+16>: 0x0000000000000000 0x0000000000404520
0x4045f0 <_ZTV8Derived2+32>: 0x000000000000000c 0xfffffffffffffff0
0x404600 <_ZTV8Derived2+48>: 0x0000000000404520 0x0000000000000000
0x404610 <_ZTV8Derived2+64>: 0x387828203a434347 0x736f702d34365f36
3,多继承,有虚基类,无虚函数
多重虚继承内存中只存在有一个基类
源代码
#include <iostream>
#include <typeinfo>
/*
多继承一个虚基类,无虚函数
*/
class Base{
public:
int vla1 = 32;
};
class Derived1:virtual public Base{
public:
int val2 = 2;
};
class Derived2:virtual public Base{
public:
int val3 = 3;
};
class Derived:public Derived1,public Derived2{
public:
int val4 = 4;
};
int main(){
Derived d;
return 0;
}
结构
内存布局
(gdb) p d
$1 = {
<Derived1> = {
<Base> = {
vla1 = 32
},
members of Derived1:
_vptr.Derived1 = 0x404608 <vtable for Derived+24>,
val2 = 2
},
<Derived2> = {
members of Derived2:
_vptr.Derived2 = 0x404620 <vtable for Derived+48>,
val3 = 3
},
members of Derived:
val4 = 4
}
(gdb) p sizeof(d)
$2 = 40
(gdb) x/10xg &d
0x61fdf0: 0x0000000000404608 0x0000000000000002
0x61fe00: 0x0000000000404620 0x0000000400000003
0x61fe10: 0x0000000000000020 0x0000000000000010
0x61fe20: 0x0000000000731850 0x00000000004013c7
0x61fe30: 0x0000000000000000 0x0000000000000036
(gdb) x/10xg 0x4045f0
0x4045f0 <_ZTV7Derived>: 0x0000000000000020 0x0000000000000000
0x404600 <_ZTV7Derived+16>: 0x00000000004044f0 0x0000000000000010
0x404610 <_ZTV7Derived+32>: 0xfffffffffffffff0 0x00000000004044f0
0x404620 <_ZTV7Derived+48>: 0x387828203a434347 0x736f702d34365f36
0x404630 <_ZTV7Derived+64>: 0x722d6865732d7869 0x697542202c307665
4,单继承,有虚基类,有虚函数
源代码
#include <iostream>
#include <typeinfo>
/*
单继承,有虚继承、有虚函数
*/
class Base{
public:
int vla1 = 8;
virtual void get(){
std::cout << "b" << std::endl;
}
};
class Derived1:virtual public Base{
public:
int val2 = 32;
virtual void get()override{
std::cout << "d1" << std::endl;
}
};
int main(){
Derived1 *d1 = new Derived1;
Base *b1 = d1;
Base &b2 = *d1;
return 0;
}
结构
内存
发生多态时,编译器将自动调整基类指针的指向(指向派生类对象的subobject所在),因此我们无法通过基类指针获取派生类静态绑定的成员。当然知道了布局之后,可以通过内存可以获取。
当调用虚函数时,通过虚表指针v_base找到thunk,经编译器调整后调用动态绑定的对象的虚函数。
(gdb) p *d1
$1 = {
<Base> = {
_vptr.Base = 0x404588 <vtable for Derived1+56>,
vla1 = 8
},
members of Derived1:
_vptr.Derived1 = 0x404568 <vtable for Derived1+24>,
val2 = 32
}
(gdb) p sizeof(*d1)
$10 = 32
(gdb) x/4xg d1 //可能编译器不会把用来填充的字节初始化吧...
0xf51b60: 0x0000000000404568 0xbaadf00d00000020
0xf51b70: 0x0000000000404588 0xbaadf00d00000008
(gdb) p *b1
$2 = {
_vptr.Base = 0x404588 <vtable for Derived1+56>,
vla1 = 8
}
(gdb) p b1
$3 = (Base *) 0xf51b70
(gdb) p b2
$4 = (Base &) @0xf51b70: {
_vptr.Base = 0x404588 <vtable for Derived1+56>,
vla1 = 8
}
(gdb) x/10xg 0x404550
0x404550 <_ZTV8Derived1>: 0x0000000000000010 0x0000000000000000
0x404560 <_ZTV8Derived1+16>: 0x00000000004044d0 0x0000000000402d90
0x404570 <_ZTV8Derived1+32>: 0xfffffffffffffff0 0xfffffffffffffff0
0x404580 <_ZTV8Derived1+48>: 0x00000000004044d0 0x0000000000402e20
0x404590 <_ZTV8Derived1+64>: 0x387828203a434347 0x736f702d34365f36
5,多重继承,有虚继承,有虚函数
源代码
#include <iostream>
#include <typeinfo>
typedef void (*Pfun)();
/*
单继承、多继承
有虚继承、有虚函数
*/
class Base{
public:
int vla1 = 8;
virtual void get(){
std::cout << "b" << std::endl;
};
};
class Derived1:virtual public Base{
public:
int val2 = 32;
virtual void get()override{
std::cout << "d1: " << std::endl;
};
};
class Derived2:virtual public Base{
public:
int val3 = 48;
virtual void get()override{std::cout << "d2" << std::endl;};
};
class Derived:public Derived1,public Derived2{
public:
int val4 = 16;
virtual void get()override{std::cout << "d" << std::endl;};
};
int main(){
Derived *d = new Derived;
Derived1 *d1 = d;
Derived2 &d2 = *d;
Base &b = *d;
std::cout << "???" << std::endl;
Pfun f1 = (Pfun)(int*)*(int*)*((int*)d+4);
Pfun f2 = (Pfun)(int*)*(int*)*((int*)d+8);
return 0;
}
结构
内存
(gdb) p *d
$14 = {
<Derived1> = {
<Base> = {
_vptr.Base = 0x405708 <vtable for Derived+88>,
vla1 = 8
},
members of Derived1:
_vptr.Derived1 = 0x4056c8 <vtable for Derived+24>,
val2 = 32
},
<Derived2> = {
members of Derived2:
_vptr.Derived2 = 0x4056e8 <vtable for Derived+56>,
val3 = 48
},
members of Derived:
val4 = 16
}
(gdb) p d
$9 = (Derived *) 0x1d1b60
(gdb) p d1
$10 = (Derived1 *) 0x1d1b60
(gdb) p &d2
$11 = (Derived2 *) 0x1d1b70
(gdb) p &b
$13 = (Base *) 0x1d1b80
(gdb) p sizeof(*d)
$3 = 48
(gdb) x/6xg d
0x1d1b60: 0x00000000004056c8 0xbaadf00d00000020
0x1d1b70: 0x00000000004056e8 0x0000001000000030
0x1d1b80: 0x0000000000405708 0xbaadf00d00000008
(gdb) p f1
$1 = (Pfun) 0x402fb0 <non-virtual thunk to Derived::get()>
(gdb) p f2
$2 = (Pfun) 0x402fc0 <virtual thunk to Derived::get()>
(gdb) x/12xg 0x4056b0 ///可能虚基类的vbase_offset没用吧..
0x4056b0 <_ZTV7Derived>: 0x0000000000000020 0x0000000000000000
0x4056c0 <_ZTV7Derived+16>: 0x0000000000405570 0x0000000000402dc0
0x4056d0 <_ZTV7Derived+32>: 0x0000000000000010 0xfffffffffffffff0
0x4056e0 <_ZTV7Derived+48>: 0x0000000000405570 0x0000000000402fb0
0x4056f0 <_ZTV7Derived+64>: 0xffffffffffffffe0 0xffffffffffffffe0
0x405700 <_ZTV7Derived+80>: 0x0000000000405570 0x0000000000402fc0
最后
还有很多地方没有深究…比如thunk…