C/C++的参数传递机制(2)

因为我的机器是32位的CPU,从上面的汇编代码可以看64Bit的变量被分成2个32Bit的参数压入栈中。这也是我们常说的,值传递会形成一个拷贝。如果是一个自定义的结构类型,并且有很多参数,那么如果用值传递,这个结构体将被分割为非常多个32Bit的逐个拷贝到栈中去,这样的参数传递效率是非常慢的。所以结构体等自定义类型,都使用引用传递,如果不希望别人修改结构体变量,可以加上const修饰,如(const MY_STRUCT&  _value);

下面来看一下Test001函数对应的反汇编代码的参数传递

__int64* _pArray = NULL;
004137E0  mov        dword ptr [_pArray],0
// Test the pointer
GetMemory(_pArray);
 mov        eax,dword ptr [_pArray]
 push        eax 
 call        GetMemory (411203h)
0041381B  add        esp,4

从上面的汇编代码可以看出,其实是0被压入到栈中作为参数,所以GetMemory(_pArray)无论做什么事,其实都与指针变量_pArray无关。GetMemory()分配的空间是让栈中的临时变量指向的,当函数退出时,栈得到恢复,结果申请的空间没有人管,就产生内存泄露的问题了。《C++ Primer》将参数传递分为引用传递和非引用传递两种,非引用传递其实可以理解为值传递。这样看来,指针传递在某种意义上也是值传递,因为传递的是指针的值(1个4BYTE的值)。值传递都不会改变传入实参的值的。而且普通的指针传递其实是改变的指针变量指向的内容。

下面再看一下Test002函数对应的反汇编代码的参数传递

__int64* _pArray = NULL;
004137E0  mov        dword ptr [_pArray],0
GetMemory(&_pArray);
004137E7  lea        eax,[_pArray]
004137EA  push        eax 
004137EB  call        GetMemory (4111FEh)

004137F0  add        esp,4

从上面的汇编代码lea eax,[_pArray] 可以看出,_pArray的地址被压入到栈中去了。

然后看一看GetMemory(&_pArray)的实现汇编代码。

0x0040159b <+0>:        push  ebp

0x0040159c <+1>:        mov    ebp,esp

0x0040159e <+3>:        sub    esp,0x18

0x004015a1 <+6>:        mov    DWORD PTR [esp],0x20

0x004015a8 <+13>:        call  0x473ef0 <_Znaj>

0x004015ad <+18>:        mov    edx,DWORD PTR [ebp+0x8]

0x004015b0 <+21>:        mov    DWORD PTR [edx],eax

0x004015b2 <+23>:        leave 

0x004015b3 <+24>:        ret 

蓝色的代码是分配临时变量空间,然后调用分配空间函数分配空间,得到的空间指针即eax.

然后红色的汇编代码即从ebp+0x8的栈上取到上面压入栈中的参数_pArray的地址.

mov DWORD PTR [edx],eax即相当于把分配的空间指针eax让edx指向,也即让_pArray指向分配的空间eax.

总之,无论是哪种参数传递方式,参数都是通过栈上的临时变量来间接参与到被调用函数的。指针作为参数,其本身的值是不可能被改变的,能够改变的是其指向的内容。引用是通过指针来实现的,所以引用和指针在效率上一样的。

C++ Primer Plus 第6版 中文版 清晰有书签PDF+源代码

读C++ Primer 之构造函数陷阱

读C++ Primer 之智能指针

读C++ Primer 之句柄类

将C语言梳理一下,分布在以下10个章节中:

Linux-C成长之路(一):Linux下C编程概要

Linux-C成长之路(二):基本数据类型

Linux-C成长之路(三):基本IO函数操作

Linux-C成长之路(四):运算符

Linux-C成长之路(五):控制流

Linux-C成长之路(六):函数要义

Linux-C成长之路(七):数组与指针

Linux-C成长之路(八):存储类,动态内存

Linux-C成长之路(九):复合数据类型

Linux-C成长之路(十):其他高级议题

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/4ce313a224e66c90c0eeb30afcc20f7e.html