`
猫太的鱼
  • 浏览: 239247 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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 呢?

#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++对象内存模型.pdf

    在 C++ 中,我们可以使用 sizeof 运算符来获取对象的内存大小,并且通过 std::cout 可以输出我们所需要的各种实验数据。为此,我们可以定义宏 define 来简化我们的实验过程。 #define DisPlayTypeSize(TypeName) ...

    C++ 内存对象布局

    ### C++内存对象布局详解 #### 一、基类对象内存布局 在C++中,对象的内存布局受到多种因素的...综上所述,C++对象的内存布局是一个复杂的过程,受到多种因素的影响。理解这些布局规则有助于更好地设计类和管理内存。

    C++对象内存布局

    ### C++对象内存布局 #### 1. 最简单的类 在C++中,理解对象的内存布局对于深入学习语言特性非常关键。通过分析一个简单的类`CTest`,我们可以更好地了解对象是如何在内存中分配和组织的。 ##### 1.1.1 赋值语句...

    深度探索C++对象模型.pdf

    C++对象模型是C++编程语言中一个核心且复杂的概念,它定义了如何在内存中表示类、对象以及它们之间的关系。理解C++对象模型对于深入掌握C++语言特性、优化代码性能以及进行高效内存管理至关重要。 #### 对象模型的...

    C++类成员和数据成员初始化总结

    在C++编程中,类成员和数据成员的初始化是一个至关重要的概念,因为它直接影响到对象的创建和使用。这里我们将深入探讨这些知识点。 首先,当创建一个C++类的对象时,对象的构造过程遵循一定的顺序。这包括两步:1)...

    深度探索c++对象模型(2012版本)

    C++对象模型是C++编程的基础,它描述了如何在内存中表示类和对象,以及它们之间的关系。首先,我们需要了解C++中的构造函数和析构函数,这两个函数在对象生命周期的开始和结束时被调用,用于初始化和清理对象的状态...

    深度探索C++对象模型 PDF

    首先,书中会详细讲解C++对象在内存中的表示方式,包括对象头、成员变量和成员函数的存储位置。理解这一部分对于优化内存使用和性能至关重要,比如了解如何通过指针或引用访问对象成员,以及如何正确地处理对象的...

    全局数据管理—使用C++类的静态成员取代全局数据

    静态数据成员只分配一次内存,这意味着它们不会随着类对象的创建和销毁而改变。因此,静态成员不占对象的内存,不会增加对象的大小。 6. **静态成员函数** 虽然题目主要关注静态数据成员,但值得一提的是,静态...

    深度探索C++对象模型 PDF中文清晰版

    《深度探索C++对象模型》是一本面向有C语言基础的读者,旨在深入解析C++这一强大编程语言中关于对象模型的专著。C++作为面向对象编程的典范,其对象模型是理解语言特性和底层机制的关键。这本书的中文清晰版提供了一...

    侯捷讲座:C++对象模型(PDF高清).rar

    数据成员存储在对象的内存空间中,而成员函数通常不直接存储在对象内,而是通过虚函数表(vtable)来实现多态调用。当类包含虚函数时,编译器会为该类生成一个虚函数表,每个对象实例都会有一个指向这个表的指针,...

    (C++)深度探索C++对象模型_C++_

    创建对象时,内存会分配给对象的数据成员,并可以调用成员函数来操作这些数据。 2. **数据成员与成员函数**:数据成员是对象的状态,而成员函数代表对象的行为。数据成员可以在类内声明为公有(public)、私有...

    深度探索C++对象模型-带目录书签.pdf

    《深度探索C++对象模型》是一本专注于C++编程语言底层机制的专业书籍,它深入剖析了C++的对象模型,帮助读者理解C++是如何在内存中表示对象、如何实现继承、多态等特性。这本书的内容涵盖了C++的核心概念,包括类、...

    vs2008下C++对象内存布局

    在Visual Studio 2008 (VS...通过深入理解VS2008下的C++对象内存布局,开发者可以更好地优化代码,避免内存相关的错误,并提升程序性能。对于大型项目,了解这些细节至关重要,因为它们直接影响程序的稳定性和效率。

    深入探索c++对象模型

    1. **C++对象模型**:C++对象模型是C++编程的基础,它描述了在C++程序中类和对象是如何在内存中存储和交互的。对象模型包括对象的生命周期、成员变量的布局、虚函数表、构造与析构过程、静态成员等内容。理解这一...

    深度探索C++对象模型(侯捷 著)

    2. 数据成员的语义:数据在对象中的布局,不同访问级别的影响。 3. 函数的语义:成员函数与非成员函数的差异,以及编译器如何生成调用代码。 4. 构造、析构和拷贝的语义:涉及对象的创建、销毁和拷贝时编译器执行的...

    深度探索C++对象模型(简体中文版).pdf

    书中详细介绍了构造函数和析构函数的语义,解释了数据成员的语义以及函数的语义,探讨了构造、析构和拷贝的语义,运行时语义以及对象模型的边缘。特别地,本书还分析了C++对象模型在运行时的语义,以及对象模型的...

    C++内存流CMemoryStream

    C++内存流CMemoryStream是MFC(Microsoft Foundation Classes)库中的一个重要组件,它允许程序在内存中读写数据,从而实现类似与文件流的操作。内存流提供了一种高效、灵活的方式来处理内存中的数据,特别是在处理...

Global site tag (gtag.js) - Google Analytics