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

原文: http://ocelot1985-163-com.iteye.com/blog/990907 

下面我们再来在 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 函数之前,需要运
行构造函数,为其成员变量赋值,这时虽然在你的程序里还没有用到,但它已经开始占用了
物理内存。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值