Functions — call / return / recursion

函式

程式語言一 · 導覽投影片

→ / space 前進  回 Esc 總覽 F 全螢幕
導覽:建框架。完整內容在 PDF,可跑範例在 index。

01為什麼要函式 · 學習地圖

把重複邏輯包起來、取名、重用(寫一次用多次,改只改一處);把大問題拆成小問題printfscanf 本身就是函式。

定義/呼叫參數 vs 引數回傳 / void傳值原型 / 作用域遞迴

本投影片只建地圖;每站的完整程式在 PDF 與 index。

02呼叫的五步流程

int add(int a, int b) { return a + b; }
... add(3, 4) ...   // a 收到 3、b 收到 4,回傳 7
① 暫停 main
把引數複製給參數(3→a、4→b)
③ 執行函式主體
return 把結果交回呼叫處
main 從原處繼續

03傳值(call by value)

參數拿到的是引數的複本。改複本,動不到呼叫端的原變數。

void inc(int v){ v = v + 1; }      // 改的是複本
int main(void){ int x = 5; inc(x); printf("%d", x); }  // 仍印 5
要改呼叫端得傳位址:void inc(int *v){ *v += 1; }inc(&x)。指標細節在「指標與記憶體」章。

04原型與作用域

函式原型
先宣告、後定義。定義放 main 後面時,前面要放原型,結尾有分號
int add(int a,int b);
區域變數作用域
函式內宣告的變數只在該函式內存在、函式結束就消失。不同函式的同名變數互不相干。

05遞迴:函式呼叫自己

void countdown(int n){
    if (n <= 0){ printf("liftoff"); return; }  // ① 基底條件:停
    printf("%d ", n);
    countdown(n - 1);                           // ② 遞迴步驟:問題變小
}

兩個必備要素:① 基底條件(何時停)② 遞迴步驟(讓問題每次更靠近基底)。

忘了基底條件 → 無窮遞迴 → 撐爆 call stack(Stack Overflow)。

06追蹤骨架:fact(4) 的 call stack

fact(4) → 4 * fact(3) fact(3) → 3 * fact(2) fact(2) → 2 * fact(1) fact(1) → return 1 ✓ 基底 遞:問題變小 歸:1→2→6→24

「遞」一路到基底,再「歸」一路回乘。每層 frame 有自己的 n。完整圖在 PDF。

07常見錯誤分類

症狀成因
有回傳型態卻沒值忘了 return,或某條路徑沒回傳
改不到呼叫端變數用了傳值(複本);要傳位址
程式崩潰 / Stack Overflow遞迴忘了基底條件,或步驟沒讓問題變小
編譯不認得函式定義放 main 後又沒寫原型,或原型漏分號
next

接著動手

PDFindex 跑 fact/sum/fib/countdown → 本地練習 5 題(max3/質數/遞迴階乘/遞迴次方/swap)→ Quizlet + 測驗卷 ≥ 90%。