2013-07-07 69 views
12

我讀過,當程序進行函數調用時,被調用函數必須知道如何返回給調用者。被調用後,被調用的函數如何返回給調用者?

我的問題是:被調用函數如何知道如何返回到調用者?是否有通過編譯器在幕後工作的機制?

+1

您應該閱讀[this](http://en.wikipedia.org/wiki/Call_stack#Structure)這是非常好的答案:[Chapter 11 - Procedures](http://pages.cs.wisc。 edu /〜smoler/x86text/lect.notes/procedures.html)如果你喜歡視頻:[黑客組裝入門(第11部分)函數棧](http://www.progamercity.net/code-tut/168- assembly-language-primer-hackers-video-series.html)你會喜歡這裏的所有視頻。 –

回答

12

編譯器遵循特定的「調用約定」,該約定被定義爲您定位的ABI的一部分。該調用約定將包括系統知道要返回的地址的方式。調用約定通常利用硬件對程序調用的支持。英特爾,例如,返回地址被壓入堆棧:

...處理器推動EIP寄存器的值(其中包含CALL指令之後的指令的偏移量)在棧上(稍後用作返回指令指針)。

...處理器從堆棧中彈出的頂部返回指令指針(偏移)到EIP寄存器並且開始:

從函數返回經由ret指令完成程序在新的指令指針處執行。

對比,對ARM,返回地址被放入鏈接寄存器:

BLBLX指令複製下一條指令的地址爲lrr14,鏈接寄存器)。

返回通常通過執行movs pc, lr將地址從鏈接寄存器複製回程序計數器寄存器來完成。

參考文獻:

  1. Intel Software Developers Manual
  2. ARM Information Center
4

這是由疊層(尤其是在Intel樣系統)成爲可能。假設我們有一個方法caller,它包含了一個本地保存的int

caller(調用target( int必須保存。它被放置在堆棧上,以及呼叫的地址。 target(可以執行其邏輯,創建自己的局部變量,並調用其他方法。它的局部變量將與電話的地址一起放入堆棧。

target(結束時,堆棧被「展開」。包含target(的局部變量的堆棧頂部被刪除。

當方法遞歸太多時,堆棧可能會變得太大,並且可能發生「堆棧溢出」。

8
  1. 編譯器知道如何調用函數以及使用哪種調用約定。例如在C中,函數的參數被壓入堆棧。調用者負責清除堆棧,所以被調用的函數不必刪除參數。其他調用約定可以包括將參數推入堆棧,被調用函數必須清除它。在這種情況下,生成的代碼是這樣的,函數可以在返回之前糾正堆棧。 Ohter調用約定可能將參數傳遞到寄存器中,因此在這種情況下,被調用函數也不必小心。

  2. CPU有調用子程序的機制。這會將當前執行地址存儲在堆棧上,然後將處理轉移到新地址。當函數完成時,它執行一個return語句,它將獲取調用者地址並在那裏恢復執行。

如果返回地址被破壞,因爲堆棧沒有正確清理uo,或者內存被覆蓋,那麼你會得到未定義的行爲。當然,具體的實施細節取決於所使用的平臺。

4

它需要被叫方和主叫方之間的合作。

主叫方同意給被叫方返回給被叫方的地址(通常通過將其推入堆棧或通過將其傳送到註冊表中),並且被叫方同意在完成時返回到該地址執行。

相關問題