探讨c++函数中的参数传递与返回值(三) 函数返回值

第三章 返回值

众所周知,函数的返回值是由eax传递出来的(浮点数则用浮点数寄存器ST0及浮点指令fld等)。

返回简单值类型的时候,eax中存储的是变量的值。

若以指针、引用做返回值,则会mov指针或引用所在内存中的地址值到eax,然后在外部接收返回值时候,在做一次mov操作。

在32位系统中,寄存器eax与地址值一样,均为32位,因此可用eax直接返回。

测试代码:

int g_int = 100;
int& g_intRef = g_int;
int FuncReturnInt()						//返回 值类型
{
004113C0  push        ebp  
004113C1  mov         ebp,esp 
004113C3  sub         esp,0C0h 
004113C9  push        ebx  
004113CA  push        esi  
004113CB  push        edi  
004113CC  lea         edi,[ebp-0C0h] 
004113D2  mov         ecx,30h 
004113D7  mov         eax,0CCCCCCCCh 
004113DC  rep stos    dword ptr es:[edi] 				//之上为函数保存环境和初始化代码
	return g_int;									
004113DE  mov         eax,dword ptr [g_int (417000h)] 	//此处直接将g_int的值传给eax
}												
004113E3  pop         edi  							//之下为函数恢复环境
004113E4  pop         esi  
004113E5  pop         ebx  
004113E6  mov         esp,ebp 
004113E8  pop         ebp  
004113E9  ret     

int* FuncReturnIntPointer()						//返回 指针类型
{
00411400  push        ebp  
00411401  mov         ebp,esp 
00411403  sub         esp,0C0h 
00411409  push        ebx  
0041140A  push        esi  
0041140B  push        edi  
0041140C  lea         edi,[ebp-0C0h] 
00411412  mov         ecx,30h 
00411417  mov         eax,0CCCCCCCCh 
0041141C  rep stos    dword ptr es:[edi] 			//之上为函数保存环境和初始化代码
	return &g_int;
0041141E  mov         eax,offset g_int (417000h) 	//将g_int的地址(offset)传给eax
}
00411423  pop         edi  						//之下为函数恢复环境
00411424  pop         esi  
00411425  pop         ebx  
00411426  mov         esp,ebp 
00411428  pop         ebp  
00411429  ret     

int& FuncReturnIntRef1()				//将值类型做引用类型 返回
{
00411440  push        ebp  
00411441  mov         ebp,esp 
00411443  sub         esp,0C0h 
00411449  push        ebx  
0041144A  push        esi  
0041144B  push        edi  
0041144C  lea         edi,[ebp-0C0h] 
00411452  mov         ecx,30h 
00411457  mov         eax,0CCCCCCCCh 
0041145C  rep stos    dword ptr es:[edi] 			//之上为函数保存环境和初始化代码
	return g_int;
0041145E  mov         eax,offset g_int (417000h) 	//将g_int的地址(offset)传给eax
}											//与返回指针类型一致
00411463  pop         edi  						//之下为函数恢复环境
00411464  pop         esi  
00411465  pop         ebx  
00411466  mov         esp,ebp 
00411468  pop         ebp  
00411469  ret

int& FuncReturnIntRef2()					//返回 引用类型
{
00411480  push        ebp  
00411481  mov         ebp,esp 
00411483  sub         esp,0C0h 
00411489  push        ebx  
0041148A  push        esi  
0041148B  push        edi  
0041148C  lea         edi,[ebp-0C0h] 
00411492  mov         ecx,30h 
00411497  mov         eax,0CCCCCCCCh 
0041149C  rep stos    dword ptr es:[edi] 		//之上为函数保存环境和初始化代码
	return g_intRef;
0041149E  mov         eax,dword ptr [g_intRef (417004h)]  //取g_intRef的内容(0x417004)
}										//赋值给eax,其内存储的是g_int的地址
004114A3  pop         edi  					//之下为函数恢复环境
004114A4  pop         esi  
004114A5  pop         ebx  
004114A6  mov         esp,ebp 
004114A8  pop         ebp  
004114A9  ret   

PS: 引用做返回值,可以做左值。

PS: 返回值为引用时,与返回值为指针时相同,要注意所引用变量的作用域。

返回局部对象的指针和引用均是不安全的行为。

 

我们还需要注意的是返回值的接收:返回值类型的函数,其eax存储的是某个值;返回指针类型的函数,其eax存储的是某个指针;返回引用类型的函数,其eax中存储的是其表示的变量的地址。

 

这里我们需要注意的一种情况

int add(int a,int b)
{
    return a+b;
}
int& nRef = add(1,2);	//此处包含了引用的初始化与接收返回值两种情况。
//这种情况是不被编译器认可的

我在网上看到有一篇分析引用做返回值的博客中,说明此种情况会被编译器警告。

经我测试,发现VS系列的编译器会直接报error C2440,也许是那篇文章的作者是用的其他编译器(如borland系列编译器)也说不定。

至于VS报错的原因,我们可以猜测一番:

虽然C2440的错误描述是说无法从type1(此例中为int)转到type2(此例中为int&),但是显然这种解释无法满足我们(int n;int& nRef= n;这种形式可是被允许的)。

函数的返回值是由eax传递出来的。也就是说add函数返回的值,没有内存地址,只是存储在一个寄存器eax中。

而引用作为一种特殊的指针,其内存空间中需要存储一个地址值,而add返回值没有地址值,所以无法处理。

这就符合了引用的规则2,引用必须与合法的存储单元关联



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值