2011-07-20 36 views
2
#include<stdio.h> 

int add(int,int); 

int main() 
{ 
    int a; 
    a=add(5,7); 
    printf("%d",a); 
} 

int add(int x,int y) 
{ 
    x=x+y; 
    return(x); 
} 

昨晚我對return語句有疑問。請參閱x是在add函數中定義的自動變量,因爲ANSI表示自動變量存在並且僅在函數內存在其使用期限,但此處return語句可以使變量a在函數外部存在。它在哪裏存儲價值?堆棧還是堆?return語句在哪裏保存其數據?

+1

在你的文章中,真正的問題是,爲什麼VALUE STILL EXISTS在'add'函數之外,而不是爲什麼變量仍然存在。你可以在'add'函數外部訪問'x'變量嗎?不,它不再存在。 – dpp

回答

9

既不在堆棧上,也不在堆上。

至少在x86架構中,函數的返回值通常被複制到eax寄存器,所以它在函數之外仍然存在。這是你不能返回一個數組而僅僅是一個指針的原因之一。

但是,您可以通過值返回結構和其他更大的變量,在這種情況下,編譯器通過使用堆棧和附加寄存器(如edx)來執行一些技巧。

+0

也許你補充說它對大/復值和浮點值不是真的。 – fyr

+0

更大的類型,如浮點/結構將被推送到堆棧 – kalyan

+0

我對你的回答並不滿意,我覺得它太具體:C是一種基於堆棧的語言,所以返回值*應該*無論架構如何,退出某個函數時的堆棧**。 現在,當你說當微架構允許它時,編譯器可以執行一些優化,比如將它推到未使用的寄存器上以加速函數返回,但這不應被視爲一般性。 – Gui13

1

add函數的返回值被複制回主函數中的'a'。在函數中聲明的自動變量存在於調用堆棧上該函數的激活記錄中,並且在將任何返回值複製回調用者變量後,在函數執行結束時刪除激活記錄。

+0

是的,C是一種「基於堆棧」的語言。尼科如果你想知道如何處理回報,你可以在這篇[維基百科文章](http://en.wikipedia.org/wiki/Call_stack)中潛水。您還可以閱讀[this](http://www.eventhelix.com/RealtimeMantra/Basics/CToAssemblyTranslation.htm)關於如何將C代碼翻譯成機器代碼的文章,將參數和返回值推入堆棧並檢索它們。 – Gui13

+1

在SO上的答案可能有所幫助,這很清楚:http://stackoverflow.com/questions/275214/scope-and-return-values-in-c – Gui13

+0

C不是基於堆棧的語言。使用堆棧只是一種可能的實現,不需要語言。 –

3

它存儲在我所知道的大多數平臺的寄存器中,但這可以通過編譯器實現進行更改。

但值得注意的是,雖然return x其中x是一個值,而不是指針,如在你的情況下是合法的,如果x是一個指針,你應該確保緩存/數據它指向的是有效的當函數返回,否則它將是一個指向無效位置的有效指針。

2

如果您是CS學生,這很可能在編程語言課程中涵蓋。無論如何,你可以看看Wikipedia

而事實上,它存儲在call stack, aka stack

+0

其實iam是一名電子學生,但很多都是編程的東西,所以我只是把我的工作轉移到了這些。 – niko

+1

@niko如果你喜歡這類東西,我推薦一本名爲「Programming Language Pragmatics」的書,這是一本驚心動魄的讀物,可能會讓你比大多數人更瞭解語言。 –

+0

謝謝ziyao – niko

5

return實際上的作用取決於代碼編譯的機器體系結構。在具有寄存器的機器上,將值x複製到寄存器。來電者將從中提取價值,然後對其進行處理。在這裏,該值被複制到名爲a的變量中。

如果此代碼是在堆棧計算機上編譯的,則返回值可能僅留在堆棧本身上。對於調用add功能的目標代碼將是這個樣子:

push 7 
push 5 
call add 
seti 0x28ccf4 

add從棧中彈出的值,加在一起,並將結果回推到堆棧中。 (如果函數處理的數據結構不會在堆棧中找到某個插槽,則會推送它們的地址。)seti是一個操作,它將從堆棧中彈出一個值並將其分配給特定地址處的整數變量。 (這裏,0x28ccf4是地址a。)。由於堆棧頂部現在將包含添加57的結果,因此a的值將變爲12

4

它有所不同。在典型情況下,您可以指望某些寄存器中返回的內容大小至少爲int(例如,x86上的eax,MIPS上的$ v0- $ v1,ARM上的r0)。

在許多機器上,較大的返回值(例如,一個具有多個成員的結構)將導致調用方在堆棧上分配空間以保存返回值,並將指向該空間的指針作爲隱藏參數傳遞給函數,或者函數在輸入函數時隱式知道該空間相對於(例如)堆棧指針的位置。

雖然問題標記爲C,但我還會提到,在C++中有一些關於返回值的特殊規則。具體來說,有一個返回值優化和一個命名的返回值優化。這些允許編譯器避開復制返回值的代碼,即使/例如(例如)副本通常會有可見的副作用。這些旨在允許在函數構造返回值的地方調用代碼將需要它的優化,而不是在函數中在本地構造它,然後將副本返回到調用代碼可以使用它的位置。