Parameter transfer mechanism of C/C ++
Recently, many people have been recruited from the company, and many C ++ programmers have been interviewed. During the interview, I will ask questions about parameter passing, especially the pointer. Because pointers are, after all, the most important advantage of C/C ++ (in some cases, they are also a disadvantage ). However, the result is that 1/3 of people basically make a mistake, but 1/3 knows it, but they do not know why. So I think it is necessary to sort out and share these knowledge points. (The following discussions are based on the default VS and GCC compilation methods. Other special compilation methods are not in the scope of this article .) C/C ++ function parameters can be transmitted in three ways: pass by value, pass bypointer, and pass by reference ). The pass-through channel for C/C ++ function parameters is stack-based. The default value is _ cdecl (C declaration mode). The parameters are pushed to the stack one by one from the right to the left, after the function is called, the caller restores the stack. (Win32API follows the stdcall parameter passing standard, which is not discussed in this article) The following is the test code 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;} // Test001void GetMemory (_ int64 * _ pBuff) {_ pBuff = new _ int64 [4];} // Test002void 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;} pointer transfer and reference transfer/ /Next take a look at the corresponding disassembly code (VS version) _ int64 nA = 0x10; 0041370E mov dword ptr [nA], 10 h 00413715 mov dword ptr [ebp-8], 0 _ int64 nB = 0x20; 0041371C mov dword ptr [nB], 20 h 00413723 mov dword ptr [ebp-18h], 0 // Test to pass by pointerSwap (& 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 referenceSwap (nA, nB); 0 041373A 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 &)> through the disassembly code above, we can see that pointer passing and reference passing are in the same mechanism. They all push the pointer value (that is, address) into the stack, call the function, and then restore the stack. Swap (nA, nB) and Swap (& nA, & nB); in fact, the assembly code is basically the same, and the addresses are retrieved from the stack. From this we can see that the reference and pointer are the same in efficiency. This is why pointers and references can achieve polymorphism. Both pointer passing and reference passing are actually changing the value of the address pointing to the memory to modify the parameter. The following is the decompiling code for passing values // Test to pass by valueSetValue (nA); 0041374A mov eax, dword ptr [ebp-8] 0041374D push eax 0041374E mov ecx, dword ptr [nA] 00413751 push ecx 00413752 call SetValue (4111EAh) 00413757 add esp, 8 because my machine is a 32-bit CPU, from the assembly code above, we can see that 64Bit variables are divided into two 32Bit parameters and pushed into the stack. This is also what we often say. value transfer forms a copy. If it is a custom structure type with many parameters, if it is passed with a value, this struct will be split into many 32Bit copies to the stack one by one, this parameter transmission efficiency is very slow. Therefore, struct and other custom types are transmitted using references. If you do not want others to modify the struct variables, you can add the const modifier, for example (const MY_STRUCT & _ value ); next let's take a look at the parameter transfer _ int64 * _ pArray = NULL for the disassembly code corresponding to the Test001 function; 004137E0 mov dword ptr [_ pArray], 0 // Test the pointer GetMemory (_ pArray); 00413812 mov eax, dword ptr [_ pArray] 00413815 push eax 00413816 call GetMemory (411203 h) 0041381B add esp, 4 from the assembly code above, it can be seen that 0 is actually pushed to the stack as a parameter, so GetMemory (_ pArray) is not related to the pointer Variable _ pArray no matter what it does. The space allocated by GetMemory () directs the temporary variables in the stack. When the function exits, the stack is restored, and the requested space is not managed by anyone, resulting in Memory leakage. C ++ Primer transfers parameters into two types: Reference Transfer and non-reference transfer. Non-reference transfer can be understood as value transfer. In this case, the pointer is passed in a sense, because it is passed the pointer value (1 4 byte value ). Value transfer does not change the value of the input parameter. In addition, normal pointer transmission is actually the content pointed to by the changed pointer variable. Next let's take a look at the parameter transfer _ int64 * _ pArray = NULL for the disassembly code corresponding to the Test002 function; 004137E0 mov dword ptr [_ pArray], 0 GetMemory (& _ pArray ); 004137E7 lea eax, [_ pArray] 004137EA push eax 004137EB call GetMemory (4111FEh) 004137F0 add esp, 4 from the assembly code lea eax, [_ pArray], we can see that, _ pArray's address is pushed into the stack. Then let's take a look at the implementation of GetMemory (& _ pArray) assembly code. 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>: the ret blue code allocates temporary variable space, and then calls the space allocation function to allocate space. The space pointer is eax. then the red assembly code is obtained from the ebp + 0x8 stack to the address of the parameter _ pArray pushed into the stack above. mov DWORD PT R [edx], and eax is equivalent to directing the allocated space pointer eax to edx, that is, directing _ pArray to the allocated space eax. in short, no matter which parameter transmission method, parameters are indirectly involved in the called function through the temporary variables on the stack. As a parameter, the value of a pointer cannot be changed. What can be changed is what it points. References are implemented by pointers. Therefore, references and pointers are of the same efficiency.