您的位置:首页 > 大数据 > 人工智能

C++愤恨者札记1——类对象作为函数参数的数据传递过程 http://blog.csdn.net/tms_li/article/details/7765626

2016-03-09 20:29 585 查看


C++愤恨者札记1——类对象作为函数参数的数据传递过程

标签: c++microsoft汇编ccompiler编译器
2012-07-20 08:07 4031人阅读 评论(5) 收藏 举报


分类:

C++愤恨者札记(9)


版权声明:本文为博主原创文章,未经博主允许不得转载。

C++愤恨者札记1——类对象作为函数参数的数据传递过程

C++繁杂的机制,加上枯燥的教科书,再加上无法回避地要使用它,注定要造就一批C++愤恨者。本文作为C++愤恨者札记系列第一篇,从汇编角度,观察类对象作为函数参数时的数据传递过程。

若没有特殊说明,编译器使用的是VC++,反汇编使用的是Windbg.下面是它们的版本号:

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86

Microsoft (R) Windows Debugger Version 6.11.0001.404 X86

测试代码如下:

[cpp] view
plain copy

class Node

{

public:

Node(){}

//Node(Node& n);

int data1;

int data2;

int data3;

int data4;

int data5;

int data6;

int data7;

};

//Node::Node(Node &n)

//{

//}

void Fn( int a, Node n, int b )

{

n.data1 = 100;

n.data2 = 100;

a = 100;

b = 10;

}

void main()

{

Node n;

Fn(1, n, 2);

}

--------------------------------------------------

未使用拷贝构造函数时,调用Fn的反汇编代码:

[plain] view
plain copy

00fa1421 6a02 push 2 ;第三个参数入栈

00fa1423 83ec1c sub esp,1Ch ;为Node n分配栈内存, 注意,构造函数Node(),并没调用

00fa1426 b907000000 mov ecx,7 ;rep循环次数

00fa142b 8d75e0 lea esi,[ebp-20h] ;Node n地址

00fa142e 8bfc mov edi,esp ;栈空间地址

00fa1430 f3a5 rep movs dword ptr es:[edi],dword ptr [esi] ;把n内容拷贝到栈空间上

;A5 MOVS m32, m32 Move doubleword

;at address DS:(E)SI to address ES:(E)DI

00fa1432 6a01 push 1 ;第一个参数入栈

00fa1434 e8a2fdffff call hello!ILT+470(?FnYAXHVNodeHZ) (00fa11db)

00fa1439 83c424 add esp,24h ;恢复栈平衡,4+1CH+4=24H

类对象参数位于栈上,是通过sub esp size来分配的。数据是通过内存拷贝来初始化。

--------------------------------------------------

使用拷贝构造函数时,即上面代码把注释去掉,调用Fn的反汇编代码:

[plain] view
plain copy

01002406 6a02 push 2 ;第三个参数入栈

01002408 83ec1c sub esp,1Ch ;开辟栈空间

0100240b 8bcc mov ecx,esp ;栈内存首址保存在ecx中,拷贝构造函数的this指针

0100240d 8d45e0 lea eax,[ebp-20h] ;实参地址

01002410 50 push eax ;作为拷贝构造函数的参数

01002411 e8d4edffff call hello!ILT+485(??0NodeQAEAAV0Z) (010011ea) ;拷贝构造函数,替换了rep movs内存拷贝

01002416 6a01 push 1 ;第一个参数入栈

01002418 e8beedffff call hello!ILT+470(?FnYAXHVNodeHZ) (010011db)

0100241d 83c424 add esp,24h ;恢复栈平衡

类参数仍然位于栈上,也是通过sub esp size来分配的。数据是通过拷贝构造函数初始化的,C++的机制就是繁多--||。

--------------------------------------------------

下面是Fn的反汇编结果,它可不管Node n是怎么初始化的,只要把它在栈上的位置找到就OK啦。

[plain] view
plain copy

hello!Fn:

00a41a60 55 push ebp | old ebp | ebp

00a41a61 8bec mov ebp,esp |-------------|

| ret address | ebp+4

00a41a63 81ecc0000000 sub esp,0C0h |-------------|

| int a | ebp+8

00a41a69 53 push ebx |-------------|

00a41a6a 56 push esi | Node n | ebp+0CH

00a41a6b 57 push edi |-------------|

| int b | ebp+28H

00a41a6c 8dbd40ffffff lea edi,[ebp-0C0h]

00a41a72 b930000000 mov ecx,30h

00a41a77 b8cccccccc mov eax,0CCCCCCCCh

00a41a7c f3ab rep stos dword ptr es:[edi] ;以是为局部变量空间初始化,debug版特有的

00a41a7e c7450c64000000 mov dword ptr [ebp+0Ch],64h ;n.data1 = 100; 显示ebp+0Ch是参数n的起始地址

00a41a85 c7451064000000 mov dword ptr [ebp+10h],64h ;n.data2 = 100;

00a41a8c c7450864000000 mov dword ptr [ebp+8],64h ;a = 100;

00a41a93 c745280a000000 mov dword ptr [ebp+28h],0Ah ;b = 10;

00a41a9a 5f pop edi

00a41a9b 5e pop esi

00a41a9c 5b pop ebx

00a41a9d 8be5 mov esp,ebp

00a41a9f 5d pop ebp

00a41aa0 c3 ret

--------------------------------------------------

总结:

类对象做为函数参数时,是被存放在栈上的,不影响实参的数据。

若未重写拷贝构造函数,类的其它构造函数将不会被调用。形参的数据是通过内存拷贝传递的。若重写了,拷贝构造函数将会在初始化形参时被调用,不再进行内存拷贝工作。

补充1:

如果你手贱,添加了一个空的拷贝构造函数,那么默认的拷贝构造函数,即那个rep movs拷贝内存的,将会被抹死掉,你的参数将得不到初始化。下面的代码将得不到想要的结果。

[cpp] view
plain copy

#include <iostream>

using namespace std;

class Node

{

public:

Node(){}

Node(Node& n)

{

/*啥都不做*/

}

int data;

};

void ShowNodeData( Node n)

{

cout << n.data << endl;

}

void main()

{

Node n;

n.data = 100;

ShowNodeData(n); //参数得不到初始化
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: