C++对象中数据成员的内存分布

[size=medium]下面我们再来在 C++类中内存分布情况。[/size]
class c1
{
public:
static int nCount;
int nValue;
char c;
c1();
virtual ~c1();
int getValue(void);
virtual void foo(void);
static void addCount();
}

[size=medium]我们可以通过 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 呢?[/size]
#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;
}

[size=medium]这是一个 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 函数之前,需要运
行构造函数,为其成员变量赋值,这时虽然在你的程序里还没有用到,但它已经开始占用了
物理内存。[/size]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值