C|深入理解函數返回值是如何保存的

我們知道,函數不能嵌套定義,但是可以嵌套調用的,函數的調用時,相關數據壓入棧空間,返回時,相關數據從棧空間彈出。而對於函數的返回值,既然被函數的棧幀空間都釋放了,那怎樣將值返回給主調函數呢?

先來看返回各類值的一個例子:

<code>#include <iostream>
using namespace std;

char getChar(){\treturn 'c';}
short getShort(){ return 2;}
int getInt(){ return 0x01020304;}
int* getP(){ int* p=new int(5); return p;}
float getFloat(){ return 5.1;}
double getDouble(){ return 5.2;}

typedef struct Student{
\tint id;
\tchar name[12];
\tdouble score;
}stu;
stu getStu(){ stu s={12,"wwu",88};return s;}

int main()
{
\tchar c\t= getChar();
\tshort si= getShort();
\tint ii\t= getInt();
\tint* p = getP();
\tfloat f = getFloat();
\tdouble d= getDouble();
\tstu s = getStu();

getchar();
\treturn 0;
}/<iostream>/<code>

1 返回整數值

<code>20:       char c  = getChar();
0040126E call @ILT+45(getChar) (00401032)
00401273 mov byte ptr [ebp-4],al/<code>

返回的整數值保存在寄存器EAX的AL中

C|深入理解函數返回值是如何保存的

2 返回short整型

<code>

返回的整數值保存在寄存器ax中

3 返回整型

<code>

返回的整數值保存在寄存器eax中

4 返回指針

<code>23:       int* p  = getP();
00401287 call @ILT+20(getP) (00401019)
0040128C mov dword ptr [ebp-10h],eax/<code>

返回的指針變量保存在寄存器eax中

5 返回浮點數

<code>24:       float f = getFloat();
0040128F call @ILT+15(getFloat) (00401014)
00401294 fstp dword ptr [ebp-14h]
25: double d= getDouble();
00401297 call @ILT+25(getDouble) (0040101e)
0040129C fstp qword ptr [ebp-1Ch]/<code>

CPU中集成了一個FPU(Float Point Unit,浮點運算單元,也稱協處理器),專用於浮點運算,FPU中包含有8個寄存器,分別為ST0~ST7。

FSTP 指令將 ST(0) 寄存器中的值複製到目標操作數,目標操作數可以是內存位置或 FPU 寄存器堆棧中的另一個寄存器。將值存儲到內存時,值會轉換成單精度或雙精度實數格式。然後彈出寄存器堆棧。為了彈出寄存器堆棧,處理器將 ST(0) 寄存器標記為空,並使堆棧指針 (TOP) 遞增 1。FSTP 指令還可以按擴展的實數格式在內存中存儲值。

6 返回自定義類型

<code>26:       stu s   = getStu();
0040129F lea eax,[ebp-64h]
004012A2 push eax
004012A3 call @ILT+10(getStu) (0040100f)
004012A8 add esp,4
004012AB mov esi,eax
004012AD mov ecx,6
004012B2 lea edi,[ebp-4Ch]
004012B5 rep movs dword ptr [edi],dword ptr [esi]
004012B7 mov ecx,6
004012BC lea esi,[ebp-4Ch]
004012BF lea edi,[ebp-34h]
004012C2 rep movs dword ptr [edi],dword ptr [esi]/<code>

對於寄存器eax無法保存返回的數據類型時,在函數調用前,編譯器會預先將要返回的數據所需要的內存空間使用自己的棧幀空間(主調函數的棧空間)保留出來,這樣當被調函數在退出時,將返回值的數據複製到預先分配的主調函數的內存棧空間,以這個臨時空間的首地址作為首地址作為返回值。由於這個臨時空間是調用者函數內部的棧空間,所以說是可用的。

綜上,被調函數的返回值通過以下三類來保存:

1 CPU寄存器;

2 EPU寄存器;

3 主調函數的棧空間。

-End-


分享到:


相關文章: