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

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

测试代码如下:
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的反汇编代码:

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的反汇编代码:
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啦。

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拷贝内存的,将会被抹死掉,你的参数将得不到初始化。下面的代码将得不到想要的结果。

#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);	//参数得不到初始化
}
	




92讲视频课+16大项目实战+源码+¥800元课程礼包+讲师社群1V1答疑+社群闭门分享会=99元   为什么学习数据分析?       人工智能、大数据时代有什么技能是可以运用在各种行业的?数据分析就是。       从海量数据中获得别人看不见的信息,创业者可以通过数据分析来优化产品,营销人员可以通过数据分析改进营销策略,产品经理可以通过数据分析洞察用户习惯,金融从业者可以通过数据分析规避投资风险,程序员可以通过数据分析进一步挖掘出数据价值,它和编程一样,本质上也是一个工具,通过数据来对现实事物进行分析和识别的能力。不管你从事什么行业,掌握了数据分析能力,往往在其岗位上更有竞争力。    本课程共包含五大模块: 一、先导篇: 通过分析数据分析师的一天,让学员了解全面了解成为一个数据分析师的所有必修功法,对数据分析师不在迷惑。   二、基础篇: 围绕Python基础语法介绍、数据预处理、数据可视化以及数据分析与挖掘......这些核心技能模块展开,帮助你快速而全面的掌握和了解成为一个数据分析师的所有必修功法。   三、数据采集篇: 通过网络爬虫实战解决数据分析的必经之路:数据从何来的问题,讲解常见的爬虫套路并利用三大实战帮助学员扎实数据采集能力,避免没有数据可分析的尴尬。   四、分析工具篇: 讲解数据分析避不开的科学计算库Numpy、数据分析工具Pandas及常见可视化工具Matplotlib。   五、算法篇: 算法是数据分析的精华,课程精选10大算法,包括分、聚、预测3大型,每个算法都从原理和案例两个角度学习,让你不仅能用起来,了解原理,还能知道为什么这么做。
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页