- [ ] 2022/3/1(此处讨论的不是返回值和返回指针/引用的情况,而是当返回不同数据长度,编译器是如何指定返回方式的
不同编译器,处理的情况不同,下面的分析是基于VS2019的msvc编译器:
- 返回值长度小于4字节,通过一个寄存器带出被调函数的栈空间;
- 返回值长度在4到8字节之间,通过两个寄存器带出;
- 返回值长度大于8字节,通过产生临时量带出,因为寄存器已经没有足够的内存存放了;另外,该临时量是在函数被调用前就创建好,把其地址压入栈中,函数被调用时对临时量赋值
好,以下开始分析:
1)函数返回宽度为4字节的值时(如返回内置类型int或指针)
int func2()
{
int a = 2;
return a;
}
//在main中调用
int aa = func2();
003E1A15 call func1 (03E1217h)
```c
int func2()
{
//此处忽略了开辟栈空间的汇编语句
int a = 2;
003E1875 mov dword ptr [a],2
return a;
003E187C mov eax,dword ptr [a]
}
003E187F pop edi
003E1880 pop esi
003E1881 pop ebx
003E1882 add esp,0CCh
003E1888 cmp ebp,esp
003E188A call __RTC_CheckEsp (03E1244h)
003E188F mov esp,ebp
003E1891 pop ebp
003E1892 ret
003E1A1A mov dword ptr [aa],eax
``
- 从以上代码可以看出,当返回值宽度为4字节,编译器会通过寄存器eax把值传回来赋给变量aa;
2)当返回值为结构体且宽度大于8字节
struct MyStruct
{
int a;
int b;
int c;
/*int d;
int e;*/
};
struct MyStruct func1()
{
MyStruct m1;
m1.a = 9;
m1.b = 8;
m1.c = 7;
/*m1.d = 6;
m1.e = 5;*/
return m1;
}
MyStruct m2 = func1();
003E1A1D lea eax,[ebp-114h]
003E1A23 push eax
003E1A24 call func1 (03E13B1h)
//以下是func1函数里面的情况
/*
MyStruct m1;
m1.a = 9;
003E1985 mov dword ptr [m1],9
m1.b = 8;
003E198C mov dword ptr [ebp-0Ch],8
m1.c = 7;
003E1993 mov dword ptr [ebp-8],7
return m1;
//下面的mov语句把返回的结构体变量的值拷贝到临时量的空间里面
003E199A mov eax,dword ptr [ebp+8]
003E199D mov ecx,dword ptr [m1]
003E19A0 mov dword ptr [eax],ecx
003E19A2 mov edx,dword ptr [ebp-0Ch]
003E19A5 mov dword ptr [eax+4],edx
003E19A8 mov ecx,dword ptr [ebp-8]
003E19AB mov dword ptr [eax+8],ecx
003E19AE mov eax,dword ptr [ebp+8]
}
*/
003E1A29 add esp,4
//以下语句:把临时量的数据拷贝回main空间的局部变量m2
003E1A2C mov ecx,dword ptr [eax]
003E1A2E mov dword ptr [ebp-100h],ecx
003E1A34 mov edx,dword ptr [eax+4]
003E1A37 mov dword ptr [ebp-0FCh],edx
003E1A3D mov eax,dword ptr [eax+8]
003E1A40 mov dword ptr [ebp-0F8h],eax
003E1A46 mov ecx,dword ptr [ebp-100h]
003E1A4C mov dword ptr [m2],ecx
003E1A4F mov edx,dword ptr [ebp-0FCh]
003E1A55 mov dword ptr [ebp-18h],edx
003E1A58 mov eax,dword ptr [ebp-0F8h]
003E1A5E mov dword ptr [ebp-14h],eax
从上面分析可知:函数返回结构体变量时,会在函数被调用前分配一段空间,先把该段空间的地址压栈,再开辟函数调用需要的栈空间,在被调函数中向该段空间赋上返回值,函数返回后,在main的栈空间再次从该段空间把数值拷贝回局部变量;