C++对象中数据成员的内存分布
来源:互联网 发布:数据连接自动开启 编辑:程序博客网 时间:2024/06/16 00:00
下面我们再来在 C++类中内存分布情况。
C++代码
class c1 { public: static int nCount; int nValue; char c; c1(); virtual ~c1(); int getValue(void); virtual void foo(void); static void addCount(); }
我们可以通过 sizeof()得到 c1 对象的大小为 12 个字节。
1、 函数 c1,~c1(),getValue,foo,addCount 为函数,其位于程序的代码段,多个对象共享,
因此不算在 c1 的 size 中。
2、 static int nCount,因为该变量为静态变量,在 c1 所定义的对象之间共享,其位于程序的
数据段。其不会随着对象数据的增加而增加。
3、 nValue 和 c 占据内存,其中 nValue 使用了 4 个字节,c 虽然使用了 1 个字节,但由于内
存对齐的缘故,其也使用了 4 个字节,这样总共占据了 8 个字节。
4、 因为有虚函数,每个类对象要有一个指向虚函数表的指针,每个对象一个,占据 4 个字
节,虚函数表是位于程序的代码段。
这样 c1 对象的大小为 12 个字节。
总结一下:
1、 静态成员和非静态成员函数,主要占据代码段内存,生成对象,不会再占用内存。
2、 非静态数据成员是影响对象占据内存大小的主要因素,随着对象数据的增加,非静态数
据成员占据的内存会相应增加。
3、 所有的对象共享一份静态数据成员,所以静态数据成员占据的内存的数量不会随着对象
的数据的增加而增加。
4、 如果对象中包含虚函数,会增加 4 个字节的空间,不论是有多少个虚函数。
对于 C++中的非内置类型的全局变量,其是属于.data 还是.bss 呢?
C++代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> class c1 { public: c1(); c1(int i); ~c1(); int n1; }; c1::c1() { n1=0; printf("n1=%d\n",n1); }; c1::c1(int i) { n1=i; printf("n1=%d\n",n1); }; c1::~c1() { ; } c1 g1; c1 g2=10; int main() { pause(); return 0; }
这是一个 C++的例子,我的问题是 g1 和 g2 分别在哪一个节呢?
按照我们原来的标准,未赋初值的全局变量 g1,将位于.bss 节,赋初值了的 g2,将位于.data
节。可细一想,又不对劲,非内置类型的全局对象,需要调用构造函数将其构造出来,不能
只通过 mmap 将其映射到内存就可以完成的。
头有些大了。
下面我来回答这个问题,实际上 g1 和 g2 全部位于.bss 节,编译器只是为其划分出了一段内
存空间。
我们来验证一下:
>nm –f sysv hello
g1 |00010a08| B | OBJECT|00000004| |.bss g2 |00010a0c| B | OBJECT|00000004| |.bss
那什么时候,对对象的成员变量赋值呢?
我们先来运行一下进程。
./hello
n1=0
n1=10
在上面的程序中,main 函数的第一句是 pause(),所以 main 函数刚一进入就停住了,而我们
依然能够看 g1 和 g2 的构造函数打印出来的结果,很显然进入 main 函数之前,运行了 g1
和 g2 的构造函数。
还记得我们前面提到的.init_array 节吗,loader 在将程序焦点转移到 main 函数之前,其会运
行.init_array 函数指针数组中的所有函数。
让我们来查看一下.init_array 中都有那些内容。
>objdump -s hello .
................
Contents of section .init_array:
108fc 7c840000 14870000
.............................................
108fc 是内存地址的一个序号,我们可以不用管它。7c840000 14870000 才是 init_array 中真 正的内容。 在这里是以小端排序,我们试着翻译一下:
应该为 7c840000 0000847c
应该为 14870000 00008714
我们可以通过查看符号表,看看这两个地址都对应着什么内容。
>nm -n -C hello
0000847c t frame_dummy 000084a4 T c1::c1() 000084e8 T c1::c1() 0000852c T c1::c1(int) 00008574 T c1::c1(int) 000085bc T c1::~c1() 000085e0 T c1::~c1() 00008604 T main 0000861c t __static_initialization_and_destruction_0(int, int) 000086c4 t __tcf_1 000086ec t __tcf_0 00008714 t global constructors keyed to _ZN2c1C2Ev 00008734 T __libc_csu_init
这样就很清楚了, 进程运行时, 在调用 main 之前, 要运行 frame_dummy 和 global constructors
Blog: http://blog.chinaunix.net/u/30686/ 69
Email:loughsky@sina.com
keyed to _ZN2c1C2Ev。
如果还有兴趣的朋友,可以尝试着对进程进行反编译看看这两个函数到底做了什么事。
我们前文说到,对于 C 程序编写的进程来讲,在运行时,只是通过 mmap 为其数据段分配
了一段虚拟内存,只有在实际用到才会分配物理内存。
而对于 C++编写的程序来讲,那些非内置类型的全局变量,由于在 main 函数之前,需要运
行构造函数,为其成员变量赋值,这时虽然在你的程序里还没有用到,但它已经开始占用了
物理内存。
- C++对象中数据成员的内存分布
- C++中对象的内存分布
- C 的内存分布
- c++对象内存中分布
- C语言中内存分布
- 多重继承及虚继承中对象内存的分布
- 多重继承及虚继承中对象内存的分布
- 多重继承及虚继承中对象内存的分布
- 多重继承及虚继承中对象内存的分布
- C++虚拟继承中对象内存的分布
- 多重继承及虚继承中对象内存的分布
- 多重继承及虚继承中对象内存的分布
- C++中数据成员,对象的内存空间
- C++对象中数据成员的布局
- C/C++的内存分布
- c程序的内存分布
- C程序的内存分布
- C程序映像中内存的空间分布
- 嵌入式linux UART驱动
- API Demos 2.3 学习笔记 (12)-- Views->Rating Bar
- 互斥锁使用范例
- 用GDB调试程序
- 我的伪ACM之路(省赛三)
- C++对象中数据成员的内存分布
- 程序员面试题精选-设计包含min函数的栈
- 嵌入式Linux之我行——按键驱动在2440上的实例开
- 看图理解单链表的反转
- 嵌入式常用的数据处理
- SQL case关键字
- 需求设计(询价信息违规处理)
- Linux串口(serial、uart)驱动程序设计
- ARM Linux中断分析(以s3c2410为例)