用汇编的眼光看C++(开篇)(一)

2014-11-24 12:47:56 · 作者: · 浏览: 4

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

很多朋友,包括我自己在内,对C++语言的很多特性不是很明白。特别是几年前找工作的时候,为了应付来自工作单位的考试,我经常逼着自己的去记住一些复杂的试题和答案。可是常常时间已过,一切又回到了原点。原来没有弄清楚的问题还是没有弄明白,一切都没有发生改变。直到若干年后,当我在编码过程中不断积累经验,尝试用汇编代码和内存数据来解释一些现象的时候,才明白有些东西其实并不复杂。也许有的朋友对汇编语言会有畏惧,其实没有必要。只要你对C语言有一些基础,对堆栈有一些印象,那么你已经拥有汇编语言的基础了。在接下来的数篇博客中,我们就会就x86汇编、数据类型、数据运行逻辑、指针、数据、类、重载运算符在汇编下是如何展开的做一些介绍,谈一些个人的看法。下面,我们就进行一些小测试,同时用汇编语言来说明一下。大家可以一起做一下。

(1)char name[] 和char* name

1:

2: void process()

3: {

00401020 push ebp

00401021 mov ebp,esp

00401023 sub esp,4Ch

00401026 push ebx

00401027 push esi

00401028 push edi

00401029 lea edi,[ebp-4Ch]

0040102C mov ecx,13h

00401031 mov eax,0CCCCCCCCh

00401036 rep stos dword ptr [edi]

4: char name_tmp[] = {"hello"};

00401038 mov eax,[string "hello" (0042201c)]

0040103D mov dword ptr [ebp-8],eax

00401040 mov cx,word ptr [string "hello"+4 (00422020)]

00401047 mov word ptr [ebp-4],cx

5: char* name_glb = "hello";

0040104B mov dword ptr [ebp-0Ch],offset string "hello" (0042201c)

6: }

00401052 pop edi

00401053 pop esi

00401054 pop ebx

00401055 mov esp,ebp

00401057 pop ebp

00401058 ret

通过上面的代码,我们可以清楚地看出两者之间的差别。"hello"字符串是一个全局只读变量,空间地址为0x0042201C。name_tmp是函数内的char数组,第4行语句下面四行表示全局数据“hello”是分两次拷贝到name_tmp的,第一次是dword、四个字节,第二次是word、两个字节。所以name_tmp共有6个字节。相比较而言,name_glb什么也没有,它只是把自己指向了全局变量而已,所以它只是一个指针而已。

(2)apple a()和apple b

假设class apple的定义为:

class apple

{

public:

apple() {}

~apple() {}

};

那么apple a()和apple b是分别怎么编译的呢?

9: void process()

10: {

00401020 push ebp

00401021 mov ebp,esp

00401023 sub esp,44h

00401026 push ebx

00401027 push esi

00401028 push edi

00401029 lea edi,[ebp-44h]

0040102C mov ecx,11h

00401031 mov eax,0CCCCCCCCh

00401036 rep stos dword ptr [edi]

11: apple a();

12: apple b;

00401038 lea ecx,[ebp-4]

0040103B call @ILT+20(apple::apple) (00401019)

13: }

00401040 lea ecx,[ebp-4]

00401043 call @ILT+10(apple::~apple) (0040100f)

00401048 pop edi

00401049 pop esi

0040104A pop ebx

0040104B add esp,44h

0040104E cmp ebp,esp

00401050 call __chkesp (004010b0)

00401055 mov esp,ebp

00401057 pop ebp

00401058 ret

为什么apple a()这边什么也没有编译呢?原因很简单,因为编译器把apple a()看成是一个extern的函数,返回值为apple。与此相对应的apple b才是函数中真正定义的临时变量,因为在下面不远处有apple的两个函数——apple的构造函数和apple的析构函数哦。

(3)(apple*) (0) -> print()

其中class apple这样定义:

class apple

{

int value;

public:

apple() {}

~apple() {}

void print() { return;}

};

如果0设置为apple*,那么访问函数print会有问题吗?

10: void process()

11: {

00401030 push ebp

00401031 mov ebp,esp

00401033 sub esp,40h

00401036 push ebx

00401037 push esi

00401038 push edi

00401039 lea edi,[ebp-40h]

0040103C mov