所有示例代码在如下环境中执行
ubuntu 16.04.4 (64位)
gcc version 5.4.0 开启std11
gdb version 7.11.1
1. 空类的大小
定义一个空类A,实例化对象a,b。分别查看a的地址和b的地址。代码如下:
#include <iostream>
class A { };
int main() {
A a;
A b;
std::cout << sizeof(A) << std::endl;
std::cout << &a << " " << &b <<std::endl;
return 0;
}
//示例1.1
输出结果:
1
0x7fff71124726 0x7fff71124727
类的实例化需要在内存中分配地址,编译器为了确保一个空类也能够实例化并且是独一无二的
所以在一个空类中加入了一个字节。
这个结果仅仅在当前示例和环境中有效,并不是四海皆准,c++标准中并没有规定加入的隐含字节
到底是多大。当然,1字节是保证空类能够实例化的最小消耗。
2. 拥有virtual函数的类A
修改上面的代码如下:
#include <iostream>
class A
{
public:
virtual void fun() {
std::cout<<"HELLO"<<std::endl;
}
};
int main() {
A a;
A b;
std::cout<<sizeof(A)<<std::endl;
std::cout<<&a<<" "<<&b<<std::endl;
return 0;
}
//示例2.1
结果:
8
0x7ffc67d214c0 0x7ffc67d214d0
大小为8,从示例1.1中可以知道两个变量地址的差值代表了实际内存分配的大小,
这里0x7ffc67d214d0与0x7ffc67d214c0的差值为16,与sizeof的结果不同。那么多出来的8字节是什么?
如果继续增加虚函数,sizeof的结果和变量地址的差值会增加吗?
继续修改示例2.1的代码,增加虚函数funa和funb,如下:
#include <iostream>
class A
{
public:
virtual void fun() {
std::cout<<"HELLO"<<std::endl;
}
virtual void funa() {
std::cout<<"HELLO funa"<<std::endl;
}
virtual void funb() {
std::cout<<"HELLO funb"<<std::endl;
}
};
int main() {
A a;
A b;
std::cout<<sizeof(A)<<std::endl;
std::cout<<&a<<" "<<&b<<std::endl;
return 0;
}
//示例2.2
运行结果如下:
8
0x7ffd5a3d4040 0x7ffd5a3d4050
结果没有变化,大小为8,差值仍然为16,现在需要做的就是确认这”多出来的“8字节内容,
以及fun funa funb到底在哪里。
3 含有virual的类A的对象模型
仍然以示例2.2为例,在本节内容中,要引入一个强大的工具GDB。
执行如下命令生成a.out:
icekylin@icekylin:~/clion_projects/untitled$ g++ -g main.cpp
icekylin@icekylin:~/clion_projects/untitled$ ls
a.out cmake-build-debug CMakeLists.txt main.cpp
icekylin@icekylin:~/clion_projects/untitled$
使用gdb调试a.out,并在代码的20打入断点,使用r命令运行程序:
icekylin@icekylin:~/clion_projects/untitled$ gdb a.out
(gdb) b main.cpp:20
Breakpoint 1 at 0x4009a5: file main.cpp, line 20.
(gdb) r
Starting program: /home/icekylin/clion_projects/untitled/a.out
Breakpoint 1, main () at main.cpp:20
20 std::cout<<sizeof(A)<<std::endl;
程序执行到这个位置后,先查看对象a,b的地址。
(gdb) p &a
$1 = (A *) 0x7fffffffdd50
(gdb) p &b
$2 = (A *) 0x7fffffffdd60
(gdb) p sizeof(A)
$3 = 8
先查看下对象a,b都包含了什么内容:
(gdb) x /16xw 0x7fffffffdd50
0x7fffffffdd50: >0x00400b80< 0x00000000 0x00400880 0x00000000
0x7fffffffdd60: >0x00400b80< 0x00000000 0x0d861c00 0x9c37e70f
0x7fffffffdd70: 0x00400ad0 0x00000000 0xf76ac830 0x00007fff
0x7fffffffdd80: 0x00000000 0x00000000 0xffffde58 0x00007fff
x命令是gdb中用来打印内存的命令,具体内容可问闷得儿(baidu)或狗剩(google)。
这一坨是什么东西?都是16进制,看不出什么含义,仅知道对象a和b都指向一个相同的内容(0x00400b80)
不同pa,在linux中还有另外一个强大的命令 pmap,用pmap可以查看指定进程的地址空间分布。
icekylin@icekylin:~/clion_projects/untitled$ pidof a.out
31272
icekylin@icekylin:~/clion_projects/untitled$ pmap -d 31272
31272: /home/icekylin/clion_projects/untitled/a.out
Address Kbytes Mode Offset Device Mapping
(1) 0000000000400000 4 r-x-- 0000000000000000 008:00012 a.out
(2) 0000000000600000 4 r---- 0000000000000000 008:00012 a.out
(3) 0000000000601000 4 rw--- 0000000000001000 008:00