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放地址。