在函数调用中,返回值如果是内置类型,由于比较小,为了速度快,通常使用eax,edx传递32位,64位返回值,当返回值类型较大时
注意,实现方式可以是任意的,这里只简单探究其中一种实现方式,来大概有个映像,可以明白,在函数调用中一般是个什么概念,但并非绝对,但估计也都差不多.
struct MyStruct
{
int SomeInt[0x20];
};
MyStruct GetStruck(int a, int b)
{
MyStruct m{};
return m;
}
int _tmain( int argc, _TCHAR* argv[] )
{
//argv;
//argc;
MyStruct k = GetStruck( 1, 2 );
return 0;
}
其,实现方式是
MyStruct* GetStruck( MyStruct* Result, int a, int b )
{
MyStruct m{};
//没办法使用Result的,否则就可以省略掉 m 到 Result的拷贝
//return 进行赋值拷贝,Result是作为一个安全内存区域所使用,甚至连返回值都不是
*Result = m;
return Result;
}
int _tmain( int argc, _TCHAR* argv[] ){
//调用以前开辟一块安全内存区域,没办法使用临时变量Temp,否则就可以省略掉 Temp 到 ReturnValue的拷贝
MyStruct Temp;
//另开辟内存存储返回值或函数表达式的值,总之此处还有一次拷贝
MyStruct ReturnValue = *GetStruck( &Temp, 1, 2 );
//再次拷贝到我们的变量中
MyStruct k = ReturnValue;return 0;
}
即,返回值的内存由调用函数开辟,将其地址作为第一个参数传递给被调函数,在被掉函数中无法直接使用此返回变量(即第一个参数)只能当return 时 拷贝变量到此返回变量中,虽然确实存在第一个参数,但是我们没有其名字,无法直接使用,唯一的使用机会就是当return语句时,可以赋值初始化(或直接列表初始化?),被调函数返回利用返回的Result再次拷贝一份作为自己表达式的值,一共发生了3次拷贝
尝试使用引用来减少拷贝次数发现竟然是一样的
const MyStruct& k = GetStruck( 1, 2 );
//竟然等同于
MyStruct k = GetStruck( 1, 2 );
MyStruct &p = k;
//依然是3次拷贝
然后是测试右值引用,依然是3次拷贝
相关测试汇编
MyStruct GetStruck(int a, int b)
{
00188660 push ebp
00188661 mov ebp,esp
00188663 sub esp,14Ch
00188669 push ebx
0018866A push esi
0018866B push edi
0018866C lea edi,[ebp-14Ch]
00188672 mov ecx,53h
00188677 mov eax,0CCCCCCCCh
0018867C rep stos dword ptr es:[edi]
0018867E mov eax,dword ptr ds:[00196000h]
00188683 xor eax,ebp
00188685 mov dword ptr [ebp-4],eax
MyStruct m{};
00188688 push 80h
0018868D push 0
0018868F lea eax,[m]
00188695 push eax
00188696 call _memset (017132Fh)
0018869B add esp,0Ch
return m;
0018869E mov ecx,20h
001886A3 lea esi,[m]
001886A9 mov edi,dword ptr [ebp+8]
001886AC rep movs dword ptr es:[edi],dword ptr [esi]
001886AE mov eax,dword ptr [ebp+8]
}
//_____________________________
00F38752 push eax
00F38753 call GetStruck (0F214E2h)
00F38758 add esp,0Ch
00F3875B mov ecx,20h
00F38760 mov esi,eax
00F38762 lea edi,[ebp-1DCh]
00F38768 rep movs dword ptr es:[edi],dword ptr [esi]
00F3876A mov ecx,20h
00F3876F lea esi,[ebp-1DCh]
00F38775 lea edi,[ebp-94h]
00F3877B rep movs dword ptr es:[edi],dword ptr [esi]
00F3877D lea ecx,[ebp-94h]
00F38783 mov dword ptr [k],ecx
//__________________________________________________________
MyStruct k = GetStruck( 1, 2 );
00A1874A push 1
00A1874C lea eax,[ebp-264h]
00A18752 push eax
00A18753 call GetStruck (0A014E2h)
00A18758 add esp,0Ch
00A1875B mov ecx,20h
00A18760 mov esi,eax
00A18762 lea edi,[ebp-1DCh]
00A18768 rep movs dword ptr es:[edi],dword ptr [esi]
00A1876A mov ecx,20h
00A1876F lea esi,[ebp-1DCh]
00A18775 lea edi,[k]
00A1877B rep movs dword ptr es:[edi],dword ptr [esi]
MyStruct &p = k;
00A1877D lea eax,[k]
00A18783 mov dword ptr [p],eax
//__________________________________________________________
MyStruct&& k = GetStruck( 1, 2 );
0018874A push 1
0018874C lea eax,[ebp-264h]
00188752 push eax
00188753 call GetStruck (01714E2h)
00188758 add esp,0Ch
0018875B mov ecx,20h
00188760 mov esi,eax
00188762 lea edi,[ebp-1DCh]
00188768 rep movs dword ptr es:[edi],dword ptr [esi]
0018876A mov ecx,20h
0018876F lea esi,[ebp-1DCh]
00188775 lea edi,[ebp-94h]
0018877B rep movs dword ptr es:[edi],dword ptr [esi]
0018877D lea ecx,[ebp-94h]
00188783 mov dword ptr [k],ecx
//__________________________________________________________
补充测试
GetStruck( 1, 2 ); 也会调用2次拷贝,被掉函数内一次,函数返回在主调函数内又一次.
int a = GetStruck( 1, 2 ).SomeInt[3]; 也是拷贝2次,证明,第一次是进入被调函数分配使用的内存,第二次是返回值或表达式值的概念.
至于通过MyStruct &&a 或者 MyStruct a 来接收 返回值,还会进行一次拷贝,