先来看c++源码:

#include <iostream>
using namespace std;
class X {
public:
int i;
public:
X(int ii = ) : i(ii) {
}
~X() {}
}; X xxx();//全局对象
int i = ;//全局变量 int main() {
}

在代码里面定义了一个全局对象xxx和一个全局变量i,main函数什么也不做。在定义全局对象xxx处打一个断点,然后在vs2010里面调试,查看对象xxx和变量i的内存,如下:
xxx的内存:

对象xxx的内存内容都被置为0,而不是无意义的数

i的内存:

可以看到,虽然i在对象xxx后面定义,但是,当断点打在定义xxx处时,它内存值已经是2了。这是因为具有初始值的全局变量,其值在连接时就被写入了文件。当用户执行该文件的时候,操作系统分析文件中的数据,将相应数据写入内存之中。因此,全局变量诞生于所在执行文件被操作系统加载之后,执行第一条代码之前。(main函数并不是程序执行的第一条代码)。

单步跟进后,我们会看到如下的构造对象xxx的汇编码:

    : X xxx();//全局对象
00BF4450 push ebp
00BF4451 mov ebp,esp
00BF4453 sub esp,0C0h
00BF4459 push ebx
00BF445A push esi
00BF445B push edi
00BF445C lea edi,[ebp-0C0h]
00BF4462 mov ecx,30h
00BF4467 mov eax,0CCCCCCCCh
00BF446C rep stos dword ptr es:[edi]
00BF446E push ;压入参数1
00BF4470 mov ecx,offset xxx ;获取对象xxx的首地址
00BF4475 call X::X ;调用对象xxx的构造函数
00BF447A push offset `dynamic atexit destructor for 'xxx' ;获取对象xxx的析构代理函数地址 ,传递给atexit函数
00BF447F call @ILT+100(_atexit) (0BF1069h) ;调用atexit函数,注册对象xxx的析构代理函数
00BF4484 add esp,4
00BF4487 pop edi
00BF4488 pop esi
00BF4489 pop ebx
00BF448A add esp,0C0h
00BF4490 cmp ebp,esp
00BF4492 call @ILT+305(__RTC_CheckEsp) (0BF1136h)
00BF4497 mov esp,ebp
00BF4499 pop ebp
00BF449A ret

上面汇编码就是全局对象xxx构造代理函数(没有源码对照)汇编码,在代理函数里面,不仅调用了对象xxx的构造函数,而且将xxx的析构函数通过调用atexit函数进行了注册。下面就来看看应用程序调用这个代理函数的过程。

对于一个应用程序,在调用main函数之前,编译器其实已经做了很多事情,因此,main函数并不是应用程序入口。当应用程序被加载时,入口代码是一个叫mainCRTStartup的函数(通过vs2010的调用堆栈可以看到),这个函数调用_tmainCRTStartup函数,_tmainCRTStartup函数又调用_initterm函数,它的c++源码如下:

static void __cdecl _initterm ( _PVFV * pfbegin, _PVFV * pfend)
{
while ( pfbegin < pfend )
{ if ( *pfbegin != NULL )
(**pfbegin)();
++pfbegin;
}
}

其中它的参数里面的_PVFV*是一个函数指针数组,编译器为每一个全局对象生成构造代理函数,而构造代理函数的地址就存储在这个函数指针数组里。_PVFV的定义原型如下:

typedef void(_cdecl *_PVFV)(void);

从定义可以看出_PVFV指向的构造代理函数为一个无参无返回值的函数。由于代理函数的类型被统一成_PVFV的形式,因此可以通过数组统一管理和执行。
当mian函数执行完毕之后,由exit来结束进程,从而终止程序的进行。全局对象的析构函数的调用也在其中。上面看到,在调用全局对象xxx的构造代理函数时,将其析构代理函数通过atexit函数进行了注册。因此,退出程序时会以注册相反的顺序调用注册的析构代理函数,并在析构代理函数中调用全局对象的真正析构函数,原理同调用构造代理函数类似。

从汇编看c++中全局对象和全局变量的更多相关文章

  1. 从汇编看c++中临时对象的析构时机

    http://www.cnblogs.com/chaoguo1234/archive/2013/05/12/3074425.html c++中,临时对象一旦不需要,就会调用析构函数,释放其占有的资源: ...

  2. 从汇编看c++中指向成员变量的指针(二)

    在从汇编看c++中指向成员变量的指针(一)中讨论的情形没有虚拟继承,下面来看看,当加入了虚拟继承的时候,指向成员变量的指针有什么变化. 下面是c++源码: #include <iostream& ...

  3. 从 php 源码看 php 中的对象

    从一个简单的例子说起: class Person { public $name; public $age; public function __construct($name, $age) { $th ...

  4. 从汇编看c++中含有虚基类对象的析构

    c++中,当继承结构中含有虚基类时,在构造对象时编译器会通过将一个标志位置1(表示调用虚基类构造函数),或者置0(表示不调用虚基类构造函数)来防止重复构造虚基类子对象.如下图菱形结构所示: 当构造类B ...

  5. 从汇编看c++中成员函数指针(一)

    下面先来看c++的源码: #include <cstdio> using namespace std; class X { public: int get1() { ; } virtual ...

  6. 从汇编看c++中指向成员变量的指针(一)

    在c++中,指向类成员变量的指针存储的并不是该成员变量所在内存的地址,而仅仅是该成员变量在该类对象中相对于对象首地址的偏移量.因此,它必须绑定到某一个对象或者对象指针上面,这里的对象和对象指针,就相当 ...

  7. 从汇编看c++中的虚拟继承及内存布局(二)

    下面是c++源码: class Top {//虚基类 public: int i; Top(int ii) { i = ii; } virtual int getTop() { cout <&l ...

  8. 从汇编看c++中的多态

    http://www.cnblogs.com/chaoguo1234/archive/2013/05/19/3079078.html 在c++中,当一个类含有虚函数的时候,类就具有了多态性.构造函数的 ...

  9. 从汇编看c++中的placement operator new

    placement operator new是重载的operator new运算符,它允许我们将对象放到一个指定的内存中.下面来看c++源码: class X { private: int _x; p ...

随机推荐

  1. C#索引器:在集合或数组中取出某一个元素 举例 _【转】

    Garmmar: [访问修饰符] 数据类型 this[参数列表] { get { 获取索引器的内容 } set { 设置索引器的内容 } } Eg: <span style="font ...

  2. jQ全选效果

    <ul id="list"> <li><label><input type="checkbox" value=&quo ...

  3. a^b的前n位数

    假设我们现在需要知道 ab  的后 n 位数或前 n 位数,简单直观的做法就是求出 ab  的值,然后在分别取前 n位或后 n位,不过在 a,b很大的情况下显然是无法存储的.所以,直接求是不可能的了. ...

  4. hdu 1042 N!(高精度乘法)

    Problem Description Given an integer N(0 ≤ N ≤ 10000), your task is to calculate N!   Input One N in ...

  5. POI操作Excel常用方法总结

    转载自:http://blog.csdn.net/xjun15/article/details/5805429 一. POI简介 Apache POI是Apache软件基金会的开放源码函式库,POI提 ...

  6. Android动态加载代码技术

    Android动态加载代码技术 在开发Android App的过程当中,可能希望实现插件式软件架构,将一部分代码以另外一个APK的形式单独发布,而在主程序中加载并执行这个APK中的代码. 实现这个任务 ...

  7. Effective Java2读书笔记-创建和销毁对象(四)

    第7条:避免使用终结方法 这一条讲的简直是不知所云.先简单记下来其中说出的几条: ①显式终止方法的典型例子有InputStream.OutputStream和java.sql.Connection上的 ...

  8. NOI十连测 第四测 T1

    思路:首先每个蚂蚁移速相同,而且碰到就转头,这其实等价于擦肩而过! 看到2n个数互不相同就觉得方便多了:枚举每个数字往左或者往右作为最慢,然后考虑其他蚂蚁有多少种走路方向. (1),走的距离大于m/2 ...

  9. SQL Server索引的维护 - 索引碎片、填充因子 <第三篇>

    实际上,索引的维护主要包括以下两个方面: 页拆分 碎片 这两个问题都和页密度有关,虽然两者的表现形式在本质上有所区别,但是故障排除工具是一样的,因为处理是相同的. 对于非常小的表(比64KB小得多), ...

  10. 用Matlab完成:从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。

    程序思路: (1)一圈人循环报数,报数报到3的人,将其置为0,表示被淘汰: (2)那么在接下去的循环中,被淘汰的人不参与报数: (3)直到仅有1人没被淘汰,并显示出他之前的编号. 程序实现如下: cl ...