c++ 返回变量和返回变量引用

区别

函数的返回时的主要区别在于,函数体返回值后是否有&,而跟return后面怎么跟的无关。

可以这么认为,return语句和函数体返回值会形成一次赋值关系(作=操作)。

函数返回的是非引用类型时,函数会创建临时对象。

sale add(const sale& lift, const sale& right) {
	//省略
	return sum;
	//函数体返回值后没有&,return会创建局部变量
}

sale& add(const sale& lift, const sale& right) {
	//省略
	return sum;
	//函数体返回值后没有&,return不会创建局部变量,而是返回一个指向sum的引用
}

返回变量

class sale {
public:
	int i = 1;
};

sale add(const sale& lift, const sale& right) {
	sale sum = lift;
	sum.i += right.i;
	cout << &sum<<endl;
	return sum;
}

int main()
{
	sale one;
	sale two;
	const sale& global = add(one, two);
	cout << &global<<endl;
	cout << global.i << endl;
}

由于返回类型为非引用,从下面的汇编也能看出,暂时放到寄存器eax里面的是,一个中间变量。所以,它是一个右值。
如果当上面语句改为sale& global = add(one, two),直接报错“非常量引用的初始值必须为左值”,编译都通不过。
反汇编如下:

sale add(const sale& lift, const sale& right) {
00D518F0 55                   push        ebp  
00D518F1 8B EC                mov         ebp,esp  
00D518F3 81 EC D0 00 00 00    sub         esp,0D0h  
00D518F9 53                   push        ebx  
00D518FA 56                   push        esi  
00D518FB 57                   push        edi  
00D518FC 8D BD 30 FF FF FF    lea         edi,[ebp-0D0h]  
00D51902 B9 34 00 00 00       mov         ecx,34h  
00D51907 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
00D5190C F3 AB                rep stos    dword ptr es:[edi]  
00D5190E A1 04 A0 D5 00       mov         eax,dword ptr [__security_cookie (0D5A004h)]  
00D51913 33 C5                xor         eax,ebp  
00D51915 89 45 FC             mov         dword ptr [ebp-4],eax  
00D51918 B9 08 C0 D5 00       mov         ecx,offset _53BED12A_consoleapplication1@cpp (0D5C008h)  
00D5191D E8 04 F9 FF FF       call        @__CheckForDebuggerJustMyCode@4 (0D51226h)  
	sale sum = lift;
00D51922 8B 45 08             mov         eax,dword ptr [lift]  
00D51925 8B 08                mov         ecx,dword ptr [eax]  
00D51927 89 4D F4             mov         dword ptr [sum],ecx  
	sum.i += right.i;
00D5192A 8B 45 0C             mov         eax,dword ptr [right]  
00D5192D 8B 4D F4             mov         ecx,dword ptr [sum]  
00D51930 03 08                add         ecx,dword ptr [eax]  
00D51932 89 4D F4             mov         dword ptr [sum],ecx  
	cout << &sum<<endl;
00D51935 8B F4                mov         esi,esp  
00D51937 68 53 12 D5 00       push        offset std::endl<char,std::char_traits<char> > (0D51253h)  
00D5193C 8B FC                mov         edi,esp  
00D5193E 8D 45 F4             lea         eax,[sum]  
00D51941 50                   push        eax  
00D51942 8B 0D AC B0 D5 00    mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0D5B0ACh)]  
00D51948 FF 15 9C B0 D5 00    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0D5B09Ch)]  
00D5194E 3B FC                cmp         edi,esp  
00D51950 E8 DB F8 FF FF       call        __RTC_CheckEsp (0D51230h)  
00D51955 8B C8                mov         ecx,eax  
00D51957 FF 15 A4 B0 D5 00    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0D5B0A4h)]  
00D5195D 3B F4                cmp         esi,esp  
00D5195F E8 CC F8 FF FF       call        __RTC_CheckEsp (0D51230h)  
	return sum;
00D51964 8B 45 F4             mov         eax,dword ptr [sum]  
}

int main()
{
00D519F0 55                   push        ebp  
00D519F1 8B EC                mov         ebp,esp  
00D519F3 81 EC 00 01 00 00    sub         esp,100h  
00D519F9 53                   push        ebx  
00D519FA 56                   push        esi  
00D519FB 57                   push        edi  
00D519FC 8D BD 00 FF FF FF    lea         edi,[ebp-100h]  
00D51A02 B9 40 00 00 00       mov         ecx,40h  
00D51A07 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
00D51A0C F3 AB                rep stos    dword ptr es:[edi]  
00D51A0E A1 04 A0 D5 00       mov         eax,dword ptr [__security_cookie (0D5A004h)]  
00D51A13 33 C5                xor         eax,ebp  
00D51A15 89 45 FC             mov         dword ptr [ebp-4],eax  
00D51A18 B9 08 C0 D5 00       mov         ecx,offset _53BED12A_consoleapplication1@cpp (0D5C008h)  
00D51A1D E8 04 F8 FF FF       call        @__CheckForDebuggerJustMyCode@4 (0D51226h)  
	sale one;
00D51A22 8D 4D F4             lea         ecx,[one]  
00D51A25 E8 AD F6 FF FF       call        sale::sale (0D510D7h)  
	sale two;
00D51A2A 8D 4D E8             lea         ecx,[two]  
00D51A2D E8 A5 F6 FF FF       call        sale::sale (0D510D7h)  
	const sale& global = add(one, two);
00D51A32 8D 45 E8             lea         eax,[two]  
00D51A35 50                   push        eax  
00D51A36 8D 4D F4             lea         ecx,[one]  
00D51A39 51                   push        ecx  
00D51A3A E8 40 F9 FF FF       call        add (0D5137Fh)  
00D51A3F 83 C4 08             add         esp,8  #调用完毕后,清除栈空间
00D51A42 89 85 04 FF FF FF    mov         dword ptr [ebp-0FCh],eax  #为中间变量分配空间
00D51A48 8B 95 04 FF FF FF    mov         edx,dword ptr [ebp-0FCh]  
00D51A4E 89 55 D0             mov         dword ptr [ebp-30h],edx  #把中间变量复制给新的变量
00D51A51 8D 45 D0             lea         eax,[ebp-30h]  #导入新变量的地址
00D51A54 89 45 DC             mov         dword ptr [global],eax  #把这个地址给global,因为引用本质是指针
	cout << &global<<endl;
00D51A57 8B F4                mov         esi,esp  
00D51A59 68 53 12 D5 00       push        offset std::endl<char,std::char_traits<char> > (0D51253h)  
00D51A5E 8B FC                mov         edi,esp  
00D51A60 8B 45 DC             mov         eax,dword ptr [global]  
00D51A63 50                   push        eax  
00D51A64 8B 0D AC B0 D5 00    mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0D5B0ACh)]  
00D51A6A FF 15 9C B0 D5 00    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0D5B09Ch)]  
00D51A70 3B FC                cmp         edi,esp  
00D51A72 E8 B9 F7 FF FF       call        __RTC_CheckEsp (0D51230h)  
00D51A77 8B C8                mov         ecx,eax  
00D51A79 FF 15 A4 B0 D5 00    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0D5B0A4h)]  
00D51A7F 3B F4                cmp         esi,esp  
00D51A81 E8 AA F7 FF FF       call        __RTC_CheckEsp (0D51230h)  
	cout << global.i << endl;
00D51A86 8B F4                mov         esi,esp  
00D51A88 68 53 12 D5 00       push        offset std::endl<char,std::char_traits<char> > (0D51253h)  
00D51A8D 8B FC                mov         edi,esp  
00D51A8F 8B 45 DC             mov         eax,dword ptr [global]  
00D51A92 8B 08                mov         ecx,dword ptr [eax]  
00D51A94 51                   push        ecx  
00D51A95 8B 0D AC B0 D5 00    mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0D5B0ACh)]  
00D51A9B FF 15 A0 B0 D5 00    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0D5B0A0h)]  
00D51AA1 3B FC                cmp         edi,esp  
00D51AA3 E8 88 F7 FF FF       call        __RTC_CheckEsp (0D51230h)  
00D51AA8 8B C8                mov         ecx,eax  
00D51AAA FF 15 A4 B0 D5 00    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0D5B0A4h)]  
00D51AB0 3B F4                cmp         esi,esp  
00D51AB2 E8 79 F7 FF FF       call        __RTC_CheckEsp (0D51230h)  
}

主函数中改成sale global = add(one, two);,那么汇编的变化为:

010E1A32 8D 45 E8             lea         eax,[two]  
010E1A35 50                   push        eax  
010E1A36 8D 4D F4             lea         ecx,[one]  
010E1A39 51                   push        ecx  
010E1A3A E8 6D F9 FF FF       call        add (010E13ACh)  
010E1A3F 83 C4 08             add         esp,8  
010E1A42 89 85 10 FF FF FF    mov         dword ptr [ebp-0F0h],eax  #分配空间,装入值。但只分配一次空间。
010E1A48 8B 95 10 FF FF FF    mov         edx,dword ptr [ebp-0F0h]  
010E1A4E 89 55 DC             mov         dword ptr [global],edx  

主函数中改成const sale global = add(one, two);,汇编和上面这个一样。

返回变量引用

class sale {
public:
	int i = 1;
};

sale& add(const sale& lift, const sale& right) {
	sale sum = lift;
	sum.i += right.i;
	cout << &sum<<endl;
	return sum;
}

int main()
{
	sale one;
	sale two;
	sale& global = add(one, two);
	cout << &global<<endl;
	cout << global.i << endl;
}

返回了局部变量的引用,这种用法肯定是错的,但这里观察汇编代码就好。

sale& add(const sale& lift, const sale& right) {
012B18F0 55                   push        ebp  
012B18F1 8B EC                mov         ebp,esp  
012B18F3 81 EC D0 00 00 00    sub         esp,0D0h  
012B18F9 53                   push        ebx  
012B18FA 56                   push        esi  
012B18FB 57                   push        edi  
012B18FC 8D BD 30 FF FF FF    lea         edi,[ebp-0D0h]  
012B1902 B9 34 00 00 00       mov         ecx,34h  
012B1907 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
012B190C F3 AB                rep stos    dword ptr es:[edi]  
012B190E A1 04 A0 2B 01       mov         eax,dword ptr [__security_cookie (012BA004h)]  
012B1913 33 C5                xor         eax,ebp  
012B1915 89 45 FC             mov         dword ptr [ebp-4],eax  
012B1918 B9 08 C0 2B 01       mov         ecx,offset _53BED12A_consoleapplication1@cpp (012BC008h)  
012B191D E8 04 F9 FF FF       call        @__CheckForDebuggerJustMyCode@4 (012B1226h)  
	sale sum = lift;
012B1922 8B 45 08             mov         eax,dword ptr [lift]  
012B1925 8B 08                mov         ecx,dword ptr [eax]  
012B1927 89 4D F4             mov         dword ptr [sum],ecx  
	sum.i += right.i;
012B192A 8B 45 0C             mov         eax,dword ptr [right]  
012B192D 8B 4D F4             mov         ecx,dword ptr [sum]  
012B1930 03 08                add         ecx,dword ptr [eax]  
012B1932 89 4D F4             mov         dword ptr [sum],ecx  
	cout << &sum<<endl;
012B1935 8B F4                mov         esi,esp  
012B1937 68 53 12 2B 01       push        offset std::endl<char,std::char_traits<char> > (012B1253h)  
012B193C 8B FC                mov         edi,esp  
012B193E 8D 45 F4             lea         eax,[sum]  
012B1941 50                   push        eax  
012B1942 8B 0D AC B0 2B 01    mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (012BB0ACh)]  
012B1948 FF 15 9C B0 2B 01    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (012BB09Ch)]  
012B194E 3B FC                cmp         edi,esp  
012B1950 E8 DB F8 FF FF       call        __RTC_CheckEsp (012B1230h)  
012B1955 8B C8                mov         ecx,eax  
012B1957 FF 15 A4 B0 2B 01    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (012BB0A4h)]  
012B195D 3B F4                cmp         esi,esp  
012B195F E8 CC F8 FF FF       call        __RTC_CheckEsp (012B1230h)  
	return sum;
012B1964 8D 45 F4             lea         eax,[sum]  #这里不一样了,之前是eax装内容,现在是直接装地址。就是相当于返回引用啦。
}


int main()
{
012B19F0 55                   push        ebp  
012B19F1 8B EC                mov         ebp,esp  
012B19F3 81 EC E8 00 00 00    sub         esp,0E8h  
012B19F9 53                   push        ebx  
012B19FA 56                   push        esi  
012B19FB 57                   push        edi  
012B19FC 8D BD 18 FF FF FF    lea         edi,[ebp-0E8h]  
012B1A02 B9 3A 00 00 00       mov         ecx,3Ah  
012B1A07 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
012B1A0C F3 AB                rep stos    dword ptr es:[edi]  
012B1A0E A1 04 A0 2B 01       mov         eax,dword ptr [__security_cookie (012BA004h)]  
012B1A13 33 C5                xor         eax,ebp  
012B1A15 89 45 FC             mov         dword ptr [ebp-4],eax  
012B1A18 B9 08 C0 2B 01       mov         ecx,offset _53BED12A_consoleapplication1@cpp (012BC008h)  
012B1A1D E8 04 F8 FF FF       call        @__CheckForDebuggerJustMyCode@4 (012B1226h)  
	sale one;
012B1A22 8D 4D F4             lea         ecx,[one]  
012B1A25 E8 AD F6 FF FF       call        sale::sale (012B10D7h)  
	sale two;
012B1A2A 8D 4D E8             lea         ecx,[two]  
012B1A2D E8 A5 F6 FF FF       call        sale::sale (012B10D7h)  
	sale& global = add(one, two);
012B1A32 8D 45 E8             lea         eax,[two]  
012B1A35 50                   push        eax  
012B1A36 8D 4D F4             lea         ecx,[one]  
012B1A39 51                   push        ecx  
012B1A3A E8 68 F9 FF FF       call        add (012B13A7h)  
012B1A3F 83 C4 08             add         esp,8  
012B1A42 89 45 DC             mov         dword ptr [global],eax  #既然等号左边也是引用,就把地址传给ptr [global]就好
	cout << &global<<endl;
012B1A45 8B F4                mov         esi,esp  
012B1A47 68 53 12 2B 01       push        offset std::endl<char,std::char_traits<char> > (012B1253h)  
012B1A4C 8B FC                mov         edi,esp  
012B1A4E 8B 45 DC             mov         eax,dword ptr [global]  
012B1A51 50                   push        eax  
012B1A52 8B 0D AC B0 2B 01    mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (012BB0ACh)]  
012B1A58 FF 15 9C B0 2B 01    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (012BB09Ch)]  
012B1A5E 3B FC                cmp         edi,esp  
012B1A60 E8 CB F7 FF FF       call        __RTC_CheckEsp (012B1230h)  
012B1A65 8B C8                mov         ecx,eax  
012B1A67 FF 15 A4 B0 2B 01    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (012BB0A4h)]  
012B1A6D 3B F4                cmp         esi,esp  
012B1A6F E8 BC F7 FF FF       call        __RTC_CheckEsp (012B1230h)  
	cout << global.i << endl;
012B1A74 8B F4                mov         esi,esp  
012B1A76 68 53 12 2B 01       push        offset std::endl<char,std::char_traits<char> > (012B1253h)  
012B1A7B 8B FC                mov         edi,esp  
012B1A7D 8B 45 DC             mov         eax,dword ptr [global]  
012B1A80 8B 08                mov         ecx,dword ptr [eax]  
012B1A82 51                   push        ecx  
012B1A83 8B 0D AC B0 2B 01    mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (012BB0ACh)]  
012B1A89 FF 15 A0 B0 2B 01    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (012BB0A0h)]  
012B1A8F 3B FC                cmp         edi,esp  
012B1A91 E8 9A F7 FF FF       call        __RTC_CheckEsp (012B1230h)  
012B1A96 8B C8                mov         ecx,eax  
012B1A98 FF 15 A4 B0 2B 01    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (012BB0A4h)]  
012B1A9E 3B F4                cmp         esi,esp  
012B1AA0 E8 8B F7 FF FF       call        __RTC_CheckEsp (012B1230h)  
}

如果在主函数里改成sale global = add(one, two),那么汇编就会变成:

00381A32 8D 45 E8             lea         eax,[two]  
00381A35 50                   push        eax  
00381A36 8D 4D F4             lea         ecx,[one]  
00381A39 51                   push        ecx  
00381A3A E8 68 F9 FF FF       call        add (03813A7h)  
00381A3F 83 C4 08             add         esp,8  
00381A42 8B 10                mov         edx,dword ptr [eax]  #将引用指向的数据取出到edx
00381A44 89 55 DC             mov         dword ptr [global],edx #再把值放到global

这是因为此时global不是引用(引用本质是指针),所以global直接放值。而之前是sale& global = add(one, two),所以global放地址。

posted @ 2019-07-14 18:00  allMayMight  阅读(430)  评论(0编辑  收藏  举报