近来公司招人较多,由此面试了非常多的C++程序员。面试时,我都会问到参数传递的相关问题,尤其侧重指针。因为指针毕竟是C/C++最重要的一个优势(在某种情况下也可以说是劣势)。但其结果是,1/3的人基本上讲错了,1/3的知其然却不知其所以然。所以我觉得有必要把这些知识点梳理下,分享出来。(下面的讨论都是基于VS和GCC的默认编译方式,其他特殊编译方式不在本文作用范围内。)
C/C++函数参数的传递方式有三种:值传递(pass by value)、指针传递(pass bypointer)、引用传递(pass by reference)。
C/C++函数参数的传递通道是通过堆栈传递,默认遵循__cdecl(C声明方式),参数由调用者从右往左逐个压入堆栈,在函数调用完成之后再由调用者恢复堆栈。(Win32API遵循stdcall传参规范的,不在本文讨论范围)
下面是测试代码
void Swap(__int64* _pnX, __int64* _pnY) { __int64 nTemp = *_pnX; *_pnX = *_pnY; *_pnY = nTemp; } void Swap(__int64& _nX, __int64& _nY) { __int64 nTemp = _nX; _nX = _nY; _nY = nTemp; } void SetValue(__int64 _nX) { __int64 nTemp = _nX; } // Test001 void GetMemory(__int64* _pBuff) { _pBuff = new __int64[4]; } // Test002 void GetMemory(__int64** _ppBuff) { *_ppBuff = new __int64[4]; } int _tmain(int argc, _TCHAR* argv[]) { __int64 nA = 0x10; __int64 nB = 0x20; // Test to pass by pointer Swap(&nA, &nB); // Test to pass by reference Swap(nA, nB); // Test to pass by value SetValue(nA); // Test the pointer that points the pointer __int64* _pArray = NULL; GetMemory(&_pArray); delete[] _pArray; _pArray = NULL; // Test the pointer GetMemory(_pArray); return 0; } |
指针传递和引用传递
// 下面看一下对应的反汇编的代码(VS版) __int64 nA = 0x10; 0041370E mov dword ptr [nA],10h 00413715 mov dword ptr [ebp-8],0 __int64 nB = 0x20; 0041371C mov dword ptr [nB],20h 00413723 mov dword ptr [ebp-18h],0 // Test to pass by pointer Swap(&nA, &nB); 0041372A lea eax,[nB] 0041372D push eax 0041372E lea ecx,[nA] 00413731 push ecx 00413732 call Swap (4111E5h) 00413737 add esp,8 // Test to pass by reference Swap(nA, nB); 0041373A lea eax,[nB] 0041373D push eax 0041373E lea ecx,[nA] 00413741 push ecx 00413742 call Swap (4111E0h) 00413747 add esp,8 // GCC版 0x00401582 <+30>: lea eax,[esp+0x18] 0x00401586 <+34>: mov DWORD PTR [esp+0x4],eax 0x0040158a <+38>: lea eax,[esp+0x1c] 0x0040158e <+42>: mov DWORD PTR [esp],eax 0x00401591 <+45>: call 0x401520 <Swap(int*, int*)> 0x00401596 <+50>: lea eax,[esp+0x18] 0x0040159a <+54>: mov DWORD PTR [esp+0x4],eax 0x0040159e <+58>: lea eax,[esp+0x1c] 0x004015a2 <+62>: mov DWORD PTR [esp],eax 0x004015a5 <+65>: call 0x401542 <Swap(int&, int&)> |