C++中按引用传递与按值传递的具体原理
一、测试代码
void Test(int& a)
{a = 10;
}void TestA(int a) {a = 5;
}int main()
{int a = 10;int* b = &a;int& c = a;a = 5;Test(a);TestA(a);*b = 3;c = 6;std::cout << a << std::endl;std::cin.get();
}
二、分析参数传递的区别
1.调用处反汇编
Test(a);
00007FF7549F1FDB lea rcx,[a]
00007FF7549F1FDF call Test (07FF7549F1465h) TestA(a);
00007FF7549F1FE4 mov ecx,dword ptr [a]
00007FF7549F1FE7 call TestA (07FF7549F146Ah)
解读:可以看到按引用传递的话,是用lea指令将变量a的地址,放到rcx寄存器中,按值传递用mov指令,将变量a地址所在的值,放到ecx寄存器重。
2.被调用处反汇编
1.Test引用传递方式汇编代码如下(示例):
void Test(int& a)
{
00007FF7549F1960 mov qword ptr [rsp+8],rcx
00007FF7549F1965 push rbp
00007FF7549F1966 push rdi
00007FF7549F1967 sub rsp,0E8h
00007FF7549F196E lea rbp,[rsp+20h]
00007FF7549F1973 lea rcx,[__AEDBAAFC_Math@cpp (07FF754A03067h)]
00007FF7549F197A call __CheckForDebuggerJustMyCode (07FF7549F13FCh) a = 10;
00007FF7549F197F mov rax,qword ptr [a]
00007FF7549F1986 mov dword ptr [rax],0Ah
可以看到**mov qword ptr [rsp+8],rcx **将rcx的值(调用处main函数中变量a的地址)放到rsp+8表示的地址内(即Test函数中的本地变量a中),此时本地变量Test函数a中的值,其实是一个地址(指向main函数中的变量a的地址)。后续a = 10的汇编,可以看到是由两条指令完成:
mov rax,qword ptr [a]
mov dword ptr [rax],0Ah
可以看到完全将对本地变量a的操作当成指针来操作了,先取a地址内的值(main函数中a地址)复制到rax寄存器中,然后将0Ah(数值10)复制到rax值表示的地址(即main函数a地址)中。
2.TestA函数按值传递汇编如下:
00007FF7549F1A10 mov dword ptr [rsp+8],ecx
00007FF7549F1A14 push rbp
00007FF7549F1A15 push rdi
00007FF7549F1A16 sub rsp,0E8h
00007FF7549F1A1D lea rbp,[rsp+20h]
00007FF7549F1A22 lea rcx,[__AEDBAAFC_Math@cpp (07FF754A03067h)]
00007FF7549F1A29 call __CheckForDebuggerJustMyCode (07FF7549F13FCh) a = 5;
00007FF7549F1A2E mov dword ptr [a],5
同样的,mov dword ptr [rsp+8],ecx可以看到将ecx寄存器中值放到rsp+8表示的地址内(即TestA函数中的本地变量a中)
最后a =10;的汇编与上面完全不同,只有一条mov指令,将数值5直接复制到本地变量a表示的地址。
总结
可以看出C++中按引用传递,与按值传递,编译器给生成两种不同的指令。按引用传递,传递的是变量的地址,有点类似指针传递(但是指针传递,需要新建指针变量,以按值的方式传递指针值,相当于间接引用),按引用传递比按指针传递更加简化了。
最后可以看看,引用变量相关的汇编,也可以看出引用其实内部都是按地址处理的。
int* b = &a;
00007FF7549F1FC4 lea rax,[a]
00007FF7549F1FC8 mov qword ptr [b],rax int& c = a;
00007FF7549F1FCC lea rax,[a]
00007FF7549F1FD0 mov qword ptr [c],rax
*b = 3;
00007FF7549F1FEC mov rax,qword ptr [b]
00007FF7549F1FF0 mov dword ptr [rax],3 c = 6;
00007FF7549F1FF6 mov rax,qword ptr [c]
00007FF7549F1FFA mov dword ptr [rax],6