出自《程序員的自我修養-鏈接、裝載與庫》P299
eax是函數傳遞返回值的一個通道。
1.對於小於4個字節的數據函數將返回值存儲在eax中。
2.5~8個字節對象的情況調用慣例都是采用eax和edx的聯合返回方式進行。
3.大於8個字節的返回類型,用一下代碼測試:
1 typedef struct big_thing 2 { 3 char buf[128]; 4 }big_thing; 5 6 big_thing return_test() 7 { 8 big_thing b; 9 b.buf[] = 0; 10 return b; 11 } 12 13 int main() 14 { 15 big_thing n = return_test();
16 }
- 首先main函數在棧額外開辟了一片空間,並將這塊空間的一部分作為傳遞返回值的臨時對象,這里稱為temp
- 將temp對象的地址作為隱藏參數傳遞個return_test函數
- return_test 函數將數據拷貝給temp對象,並將temp對象的地址用eax傳出。
- return_test返回以后,mian函數將eax 指向的temp對象的內容拷貝給n。
如果返回值的類型的尺寸太大,c語言在函數的返回時會使用一個臨時的棧上內存作為中轉,結果返回值對象會被拷貝兩次。因而不到萬不得已,不要輕易返回大尺寸對象。
再來看看函數返回一個C++對象會如何:
1 #include <iostream> 2 using namespace std; 3 4 struct cpp_obj 5 { 6 cpp_obj() 7 { 8 cout << "ctor\n"; 9 } 10 11 cpp_obj(const cpp_obj& c) 12 { 13 cout << "copy ctor\n"; 14 } 15 16 cpp_obj& opearator=(const cpp_obj& rhs) 17 { 18 cout << "operator = \n"; 19 return *this; 20 } 21 22 ~cpp_obj() 23 { 24 cout << "dtor\n"; 25 } 26 }; 27 28 cpp_obj return_test() 29 { 30 cpp_obj b; 31 cout << "before return\n"; 32 return b; 33 } 34 int main() 35 { 36 cpp_obj n; 37 n = return_test();
38 }
運行后的輸出結果可以得出:函數返回之后,進行了一個拷貝函數的調用,以及一次operator=的調用,也就是說,仍然產生了兩次拷貝。因此C++的對象同樣會產生臨時對象。
在這段代碼中我們還能看到在c++返回一個對象時,對象要經過兩次拷貝構造函數的調用才能夠完成返回對象的傳遞,1次拷貝到棧上的臨時對象里,另一次把臨時對象拷貝到存儲返回值的對象里。在某些編譯器里,返回一個對象甚至要經過更多的步驟。
為了減少返回對象的開銷,C++提出了返回值優化(RVO)技術,可以將某些場合下的對象拷貝減少一次,例如:
1 cpp_obj return_test() 2 { 3 return cpp_obj(); 4 }
目的是直接將對象的構造在傳出時使用的臨時對象上,減少一次復制過程。