以硬體的觀念來講,函數是放在記憶體中的一群指令,而每個函數被放置在不同的記憶體,如下圖所示:
┌──────────────────────────┐
│ ┌───────────────────────┐ │
│ │IP↘ code (text) │ │
│ │ ┌───┐ ┌────┐ ┌────┐ │ │
│ │ │main( )│ │函數 A( )│ │函數 B( )│ │ │
│ │ └───┘ └────┘ └────┘ │ │
│ └───────────────────────┘ │
│ ┌──────┐┌───┐┌────────┐ │
│ │ data heap ││ stack ││ global name space │ │
│ └──────┘└───┘└────────┘ │
└──────────────────────────┘
code:放置程式碼的區塊,也叫做 text,意即程式的本文之意。
data heap:所有的區域變數都被放在這裡。
stack:此塊記憶體是給編譯程式和作業系統應用的,我們通常無法直接取用。
global name space:放置所有的靜態變數、全域變數以及外部變數。(在 UNIX 系統下這塊記憶體也叫做 bss)
一個程式佔據多大的記憶體,在 UNIX 系統下可以用 size 這個指令看出來,例如我們想看看 gcc 佔了多大的記憶體空間:
[thccy14]/usr/local/bin> size gcc
text data bss dec hex filename
57312 8192 0 65504 ffe0 gcc
可以看到 gcc 的 code 有 57312 bytes,需要 8192 bytes 的 data heap,不需要 bss。
程式的執行過程:
首先,有一個叫做IP(Instruction Pointer,CPU 會依序執行IP所指位址的指令)的暫存器會指向程式開始的地方
(通常是 main 函數),當函數呼叫發生時,硬體按照下列的流程執行程式的控制權轉移:
函數呼叫發生
↓
把目前的程式執行狀態存進 stack (含 IP) ,把函數的參數由右而左存進 stack
↓
把 IP 指向欲前往的函數
↓
前往函數
進入函數之後,函數由 stack 取得參數 (這部份的碼由編譯器自動產生)之後,開始執行,執行完畢時依以下的流程交回控制權:
函數結束
↓
將傳回值存入 stack
↓
取出 stack 中原程式執行狀態,恢復原程式執行狀態
↓
取出傳回值,儲存目前的IP
↓
繼續執行原來的程式
每呼叫一次函數, CPU 就必須浪費很多時間來處理這些記憶體的儲存和更新,會使執行的速度變慢,
所以說遞迴函數不斷地執行函數的呼叫,因此會使執行速度較慢。
一個 activation record (stack frame) 儲存程式的:
–參數與本地變數
–執行結果(回傳值)
–返回位址(return address)
–其他環境資訊(詳見第6單元)
當一個程式被呼叫執行時,系統會自動配置一個activation record (stack frame)給它儲存以上資訊,
並且push這些資訊到stack;副程式執行結束時,便從其activation record中取得返回位址,回到上一層
(當初呼叫它的)程式繼續執行下一句,而系統也在此時收回它的 A.V。
同一支執行中程式的所有副程式的 A.V,都是放在同一個 stack 上,先進後出。
有些書把這個 stack 叫做 system stack (系統堆疊)。
留言列表