統測資電類・資訊科專業科目(二)核心考點

第 8 章:函式、變數範疇與遞迴

本章統整統測程式設計中「函式與作用域」的核心工具:全域/區域變數、變數遮蔽、函式原型宣告、傳值呼叫、遞迴與 static 區域變數(111~115 年共計 7 題考古真題)。 出題老師最愛把「同名變數的全域/區域判讀」、「呼叫先於宣告的編譯錯誤」與「遞迴終止條件的精確追蹤」揉合在一起設計陷阱。 本教學將帶你掌握清晰的作用域判讀與遞迴追蹤技巧,並透過7 題真題 + 5 題模擬題的所有選項深度剖析,徹底掃除所有答題盲點!

🧩
7 大核心
函式與範疇觀念全解構
🎯
7 題真題
111~115 統測題全收錄
💡
全選項剖析
A/B/C/D 逐項深入拆解
🚀
100% 互動
點擊即答 + 展開詳解

1 變數範疇:全域變數 vs 區域變數

變數範疇(scope)決定一個變數能在程式的哪個範圍被看見、使用。可以想成學校的公告方式:寫在所有函式之外的全域變數(global variable),就像貼在校門口公佈欄的公告,整個程式檔(每個函式)都能看到、都能存取;寫在函式內部的區域變數(local variable),就像貼在某間教室黑板上的通知,只有那個函式內部看得到,出了那個函式就看不到了。

C Code:全域變數與區域變數的可見範圍
int count = 0;               // 全域變數:整個檔案都看得到

void demo(int n) {
    int local_x = n * 2;      // 區域變數:只在demo()內部有效
    printf("%d\n", local_x);
}                              // 離開demo(),local_x就不存在了

int main(void) {
    count = 5;                 // main()也能存取全域的count
    demo(count);
    return 0;
}
類型宣告位置可見範圍存在時間
全域變數所有函式之外整個原始檔的每個函式都能存取程式開始到結束
區域變數函式(或區塊)內部只在宣告它的函式(區塊)內進入該函式(區塊)才產生,離開就消失

2 變數遮蔽(shadowing)深化:同名變數在不同層級的行為

若區域變數與全域變數同名,區域變數會遮蔽(shadow)全域變數——在這個區域變數有效的範圍內,對這個名字的存取一律指向區域變數,全域的那個完全被「擋住看不見」,但並沒有被刪除,只是暫時存取不到。

C Code:同名的區域變數遮蔽全域變數
int count = 0;               // 全域變數
void demo(int n) {
    int count = 100;          // 區域變數,遮蔽了全域的count
    printf("%d\n", count);    // 印的是100,不是全域的0
}
🖥️ 終端機執行輸出結果
100
⚠️ 統測最愛的陷阱:不同函式裡的同名區域變數,彼此完全獨立

如果兩個不同的函式(例如 inc() 和 main())內都各自宣告了一個叫 sum 的區域變數,這兩個 sum 是兩塊完全不同的記憶體,互不相干——即使名字一樣,也不會互相覆蓋或影響。追蹤這類程式時,務必在每個 sum 旁邊標註「這是哪個函式的區域變數」,才不會把不同函式裡的值搞混。

3 函式原型宣告(prototype):為什麼呼叫必須先「認識」函式

C 語言的編譯器是由上往下讀原始碼的。如果一個函式在被呼叫的當下,編譯器還沒看過它的定義、也沒看過任何宣告,編譯器就完全不知道這個函式需要幾個參數、參數型態是什麼、回傳型態是什麼——這會導致編譯失敗(或至少產生嚴重警告)。函式原型宣告(prototype)就是先把函式的「簽名」(名稱+參數型態+回傳型態)預告給編譯器,讓它提早知道怎麼呼叫這個函式。

C Code:用函式原型宣告解決「呼叫先於定義」的問題
#include <stdio.h>
int square(int);              // 函式原型宣告:先預告 square() 的簽名

int main(void) {
    printf("%d", square(5));  // 此時編譯器已經知道 square() 長什麼樣子
    return 0;
}

int square(int n) {           // 完整定義寫在後面也沒關係
    return n * n;
}
💡 兩種讓編譯器「提早認識」函式的方式

1. 把函式的完整定義整個寫在呼叫它的程式碼之前。
2. 或者只先寫一行原型宣告(函式簽名加分號,不含函式本體)放在前面,完整定義可以留到後面才寫——這是實務上更常見的做法,尤其當多個函式互相呼叫時特別好用。

⚠️ 常考陷阱:原型宣告被註解掉了

統測很愛出「程式碼裡明明已經寫好了原型宣告,但前面被 // 註解掉」這種題型——正確答案往往不是重新設計程式邏輯,而是把註解符號拿掉、讓已經寫好的原型宣告生效就好,是最簡單也最容易被忽略的解法。

4 傳值呼叫在函式參數中的效果(複習與深化)

延續第7章的觀念:函式參數若是一般型態(不是指標),呼叫時傳進去的只是原始變數值的一份複本。函式內對這個參數做任何修改,都只發生在這份複本上,絕對不會影響到呼叫端的原始變數,即使參數變數跟呼叫端的變數剛好同名也一樣。

C Code:函式內對傳值參數的修改不會外洩
int inc(int xin){
    int sum = 2;
    sum = sum + xin;   // 只修改inc()自己的區域sum
    xin++;             // 只修改inc()自己收到的複本,不影響呼叫端
    return (sum);
}
int main(){
    int x = 10;
    int result = inc(x);        // x以傳值方式傳入,xin收到10的複本
    printf("%d, %d", result, x); // result=12, x依然是10(沒被xin++影響)
    return 0;
}
🖥️ 終端機執行輸出結果
12, 10
🧠 追蹤這類題目的關鍵:把「複本」跟「原始變數」用不同名字在草稿紙上分開記

即使程式碼裡呼叫端變數(如 x)跟函式參數(如 xin)名字不同很好分辨,考題常常會讓兩者名字很像甚至刻意讓函式內外都有同名的變數(見第2節),這時候務必先確認「現在改的是複本還是原始變數」,複本的任何變化都不會傳回呼叫端——除非改用第7章教的傳址呼叫(指標參數)。

5 遞迴(recursion):終止條件與呼叫層追蹤

遞迴是函式呼叫自己。寫遞迴函式一定要有能讓遞迴停下來的終止條件(base case),否則函式會一直呼叫自己、永遠停不下來,最終導致程式當機(stack overflow)。

C Code:階乘(單一終止條件)與費氏數列(兩個終止條件)
int fact(int n) {             // 遞迴範例:階乘
    if (n <= 1) return 1;     // 終止條件
    return n * fact(n - 1);   // 呼叫自己,範圍縮小
}

int fib(int n) {
    if (n == 0) return 0;     // 終止條件一
    if (n == 1) return 1;     // 終止條件二
    return fib(n - 1) + fib(n - 2);
}
💡 追蹤遞迴的訣竅:畫一個「呼叫塔」,從最深層往回算

遇到遞迴追蹤題,先寫出每一層呼叫傳入的參數是什麼、什麼時候會觸發終止條件(哪一層是最後一層),再從最深、最先返回的那一層開始,把結果一層一層往外帶回去計算,不要試圖從最外層直接心算到底。

⚠️ 陷阱:終止條件判斷的是「等於某值」還是「不等於某值」

if(n==1) return;(等於1才停止)跟 if(n!=1){...遞迴...}(不等於1才繼續遞迴,等於1就自然結束)雖然邏輯效果類似,但要特別注意:後者的寫法是「終止條件成立時,函式內部完全不執行任何動作(包含不會讓計數器繼續累加),直接返回」,很容易被誤判成「還會多執行一次」。

6 static 區域變數:跨呼叫保留狀態

一般的區域變數,每次呼叫函式都會重新產生、重新初始化,函式結束後就消失。加上 static 關鍵字後,這個區域變數只會在第一次呼叫時初始化一次,之後每次呼叫都會延續上一次結束時的值,不會重新歸零。

C Code:static 區域變數在多次呼叫之間保留數值
int nextId(void) {
    static int id = 0;   // 只在第一次呼叫時初始化,之後保留上次的值
    id++;
    return id;
}
int main(void) {
    printf("%d %d %d\n", nextId(), nextId(), nextId()); // 1 2 3
    return 0;
}
🖥️ 終端機執行輸出結果
1 2 3
宣告方式每次呼叫函式時值會不會保留到下次呼叫
一般區域變數重新產生、重新初始化❌ 不會,每次都是全新的
static 區域變數只在第一次呼叫時初始化✅ 會,延續上次結束時的值

7 函式回傳型態與精度選擇

設計函式時,回傳型態要能剛好裝得下你要回傳的結果。回傳整數用 int;回傳需要小數精確度的結果,必須用 float 或 double;完全不需要回傳值,才用 void。

C Code:整數運算混入浮點數,除法結果會是浮點數
double Func(int w, int h) {
    double A;
    A = w * h / 2.0;   // w*h先算出整數15,再除以浮點數2.0
    return A;          // 整個除法會以浮點數方式運算,得7.5
}
⚠️ 統測常考陷阱:函式原型要「同時」滿足參數與回傳型態兩項需求

題目常會同時要求「接收兩個整數」和「回傳值要有小數精確度」。作答時要逐一檢查每個選項:括號裡的參數型態對不對(int, int)、回傳型態對不對(float 或 double,不能是 int 或 void)——只要有任何一項不符合,就不是正確答案,兩項都要同時滿足。

💡 整數除法 vs 浮點數除法

15 / 2(兩個都是整數)會得到整數除法的結果 7(直接捨去小數);但只要除法算式中有任何一邊是浮點數(如 15 / 2.0),整個運算就會以浮點數方式進行,得到 7.5。判斷除法是整數除法還是浮點數除法,只需要看算式中有沒有浮點數字面常數或浮點數變數參與。

8 111~115 歷屆統測真題全選項深度剖析(共 7 題)

以下收錄統測專業科目(二)近年全部 7 題函式、變數範疇與遞迴真題。每題均提供高解析度原始考題掃描圖、互動選項按鈕與所有選項逐項深入解析:

111 統測專二 Q47 📌 概念:函式呼叫/型態轉換(整數乘法混浮點數除法)
試題來源:技專校院入學測驗中心
下列 C 語言程式碼片段執行後,變數 result 的值為何?
題幹程式碼
int a=5, b=3;
double result=0;
result=Func(a, b);

double Func(int w, int h)
{
    double A;
    A=w*h/2.0;
    return A;
}
111 統測專二 Q47 題目截圖
本題標準答案: (C) 7.5

🔍 四個選項逐項深度剖析

選項 A(0.0)錯誤解析
此答案誤以為 result 沒有真正被賦值,或誤以為函式呼叫沒有生效,直接沿用了 result 宣告時的初始值0.0,忽略了 result=Func(a,b); 這行確實會呼叫函式並把回傳值指定給 result。
選項 B(7.0)錯誤解析
此答案誤以為 w*h/2.0 是先做整數除法「15/2」得到整數7,再把7轉型成浮點數7.0,忽略了除數 2.0 本身就是浮點數常數,整個除法運算會以浮點數方式進行,不會先做整數除法捨去小數、之後才轉型。
正確答案完整推導(★ 本題標準答案)
呼叫 Func(5,3),A=w*h/2.0:先計算 w*h=15(兩個int相乘,仍是整數),再除以 2.0(浮點數字面常數),因為除數是浮點數,整個除法會以浮點數方式運算,得 15/2.0=7.5,選 (C)。
選項 D(15.0)錯誤解析
此答案誤以為 /2.0 這個除法運算完全沒有執行或被忽略了,直接把 w*h 的結果15當成最終答案(只是轉成浮點數形式15.0),忽略了程式碼明確寫了要再除以2.0這個步驟。
111 統測專二 Q48 📌 概念:函式原型宣告/回傳型態選擇
試題來源:技專校院入學測驗中心
要寫一個 BMI 函式,接收兩個整數,計算後回傳值必須有小數點後至少兩位數精確度,函式原型宣告應為下列何者?
111 統測專二 Q48 題目截圖
本題標準答案: (D) float BMI(int, int);

🔍 四個選項逐項深度剖析

選項 A 錯誤解析
函式原型的括號是空的,沒有明確列出參數型態,並沒有清楚滿足題目「接收兩個整數」這項要求(空括號在傳統寫法中甚至代表參數型態未指定,而不是明確要求剛好兩個int),跟明確寫出 (int, int) 的選項相比,不夠嚴謹地符合題意。
選項 B 錯誤解析
回傳型態是 void,代表這個函式完全沒有回傳值,但題目明確要求函式要能「回傳」帶有小數點後至少兩位精確度的計算結果,void 完全無法回傳任何數值,不符合需求。
選項 C 錯誤解析
雖然正確接收了兩個整數參數,但回傳型態是 int(整數),完全無法表示小數點後的精確度,題目明確要求「回傳值必須有小數點後至少兩位數精確度」,int 型態不可能達成。
正確答案完整推導(★ 本題標準答案)
float BMI(int, int) 同時滿足了兩項需求:括號內明確列出兩個 int 參數,符合「接收兩個整數」;回傳型態是 float,具備小數精確度,能滿足「小數點後至少兩位精確度」的要求,選 (D)。
112 統測專二 Q36 📌 概念:函式原型宣告/呼叫先於宣告的編譯錯誤
試題來源:技專校院入學測驗中心
曉華發現程式無法成功編譯,應採取下列哪一個方案來解決?
題幹程式碼
#include <stdio.h>
//void sub(int i, char *s);
int main(int argc, char *argv[]) {
    sub(argc, argv[2]);
    return 0;
}

void sub(int i, char *s){
    printf("total %d arguments, and the 2nd one is %s\n", i, s);
}
112 統測專二 Q36 題目截圖
本題標準答案: (B) 去掉行號3最前面的註解標記//

🔍 四個選項逐項深度剖析

選項 A 錯誤解析
修改 main() 的參數列表跟「sub() 函式在被呼叫時,編譯器還不知道它的簽名」這個編譯錯誤的根本原因完全無關。就算把 main() 改成空括號,sub() 呼叫先於定義、又沒有事先宣告的問題依然存在,無法解決編譯錯誤。
正確答案完整推導(★ 本題標準答案)
main() 呼叫 sub() 時,sub() 的完整定義寫在 main() 之後,且沒有生效中的函式原型宣告,編譯器在編譯到 main() 裡的 sub() 呼叫時,還不知道 sub() 的參數個數與型態,因而編譯失敗。行號3其實已經寫好了正確的原型宣告,只是被註解符號 // 關閉了,只要去掉 // 讓這行原型宣告生效,編譯器就能提前知道 sub() 的簽名,順利通過編譯,選 (B)。
選項 C 錯誤解析
刪除空白行只是排版上的調整,完全不會影響編譯器對函式呼叫與定義順序的判斷,跟能否成功編譯無關。
選項 D 錯誤解析
#include <stdlib.h> 是用來引入標準函式庫(如記憶體配置、型態轉換等)的標頭檔,跟本題「自訂函式 sub() 呼叫先於宣告」這個問題完全無關,加入這行並不能提供 sub() 的原型宣告,無法解決編譯錯誤。
112 統測專二 Q40 📌 概念:全域/區域變數/傳值呼叫/變數遮蔽
試題來源:技專校院入學測驗中心
下列何者為程式執行結果?
題幹程式碼
int sum=1, x=10;
int inc(int xin){
    int sum=2;
    sum = sum + xin;
    xin++;
    return (sum);
}
int main(){
    int sum = 3;
    sum=inc(x);
    printf("%d, %d", sum, x);
    return 0;
}
112 統測專二 Q40 題目截圖
本題標準答案: (D) 12, 10

🔍 四個選項逐項深度剖析

選項 A(11, 11)錯誤解析
此答案可能誤以為 inc() 內的區域 sum 初始值不是2、而是誤用了其他數值,導致 sum=sum+xin 算錯;同時誤以為 xin++ 這個對函式內複本的遞增,會影響到 main() 呼叫時傳入的 x,讓 x 也跟著改變。但傳值呼叫的 xin 只是 x 的複本,函式內對 xin 的任何修改都不會影響到 main() 的 x。
選項 B(13, 10)錯誤解析
x=10 算對了(沒有被修改),但 main() 收到的 sum 算錯了——可能把 inc() 內的區域 sum 初始值誤算成了3(可能跟 main() 自己的區域 sum 混淆),導致 sum=sum+xin=3+10=13。但 inc() 內宣告的是自己獨立的區域 int sum=2,跟 main() 的區域 sum 是兩個完全不同的變數,不應該互相干擾。
選項 C(12, 11)錯誤解析
main() 收到的 sum=12 算對了,但 x 算錯了——此答案誤以為 xin++ 這行對函式內部參數複本的遞增,會反映到 main() 呼叫時傳入的原始變數 x 身上,讓 x 從10變成11。但本題 inc() 的參數 xin 是一般 int 型態,屬於傳值呼叫,函式內對 xin 做任何修改都只影響複本,不會影響 main() 的 x,x 應維持原值10。
正確答案完整推導(★ 本題標準答案)
全域 sum=1, x=10;main() 內宣告了自己的區域 sum=3。呼叫 inc(x),x 以傳值方式傳入,xin 接收複本10。inc() 內宣告了自己的區域 sum=2;sum=sum+xin=2+10=12;xin++ 只影響函式內的複本,不影響 main() 的 x。inc() 回傳12給 main() 的 sum。x 本身從未被真正修改過,仍是10。輸出 "12, 10",選 (D)。
112 統測專二 Q42 📌 概念:全域變數/區域變數/變數範疇綜合判讀
試題來源:技專校院入學測驗中心
關於行號2、行號4、以及行號10的變數 sum 的敘述(程式碼與上題相同),下列何者正確?
題幹程式碼(與上題相同)
int sum=1, x=10;             // 行號2:函式外部,全域變數
int inc(int xin){
    int sum=2;                // 行號4:inc()內部,區域變數
    sum = sum + xin;
    xin++;
    return (sum);
}
int main(){
    int sum = 3;               // 行號10:main()內部,區域變數
    sum=inc(x);
    printf("%d, %d", sum, x);
    return 0;
}
112 統測專二 Q42 題目截圖
本題標準答案: (A) 行號2的sum是全域變數,行號4的sum是區域變數

🔍 四個選項逐項深度剖析

正確答案完整推導(★ 本題標準答案)
行號2寫在所有函式之外(int sum=1, x=10;),是全域變數;行號4寫在 inc() 函式內部(int sum=2;),是 inc() 專屬的區域變數;行號10寫在 main() 函式內部(int sum=3;),是 main() 專屬的區域變數。故行號2的 sum 是全域變數、行號4的 sum 是區域變數,敘述正確,選 (A)。
選項 B 錯誤解析
把全域與區域的判斷完全對調了。行號2寫在所有函式範圍之外,正是全域變數的定義位置;行號4寫在 inc() 函式的大括號內部,正是區域變數的定義位置,這個選項把兩者的判斷弄反了。
選項 C 錯誤解析
行號2確實不在任何函式內,是全域變數沒錯,但行號10是寫在 main() 函式內部宣告的 int sum=3;,屬於區域變數,並不是全域變數;行號2是全域、行號10才是區域,這個選項誤以為兩者都是區域變數,忽略了行號2寫在函式外部這個關鍵位置。
選項 D 錯誤解析
行號10的 sum 明明白白寫在 main() 函式的大括號內部宣告,屬於 main() 專屬的區域變數,只在 main() 裡有效,並不是全域變數;只有行號2(寫在所有函式之外)才是真正的全域變數,這個選項把兩者都誤判成全域變數。
113 統測專二 Q36 📌 概念:變數作用域(全域/區域變數)/編譯錯誤成因
試題來源:技專校院入學測驗中心
曉華想計算二次多項式 f(x) 的值,程式發生編譯錯誤,主要原因與更正措施為下列何者?
題幹程式碼
#include <stdio.h>

float f(float x){
    return(a*x*x+b*x+c);
}
int main(){
    float x, a=1, b=0, c=-1;
    for(x=-10; x<=10; x=x+0.1)
        printf("f(%.1f)=%.1f\n", x, f(x));
}
113 統測專二 Q36 題目截圖
本題標準答案: (C) 變數a,b,c屬於main()中的區域變數,將變數a,b,c移到行號2宣告可以解決此問題

🔍 四個選項逐項深度剖析

選項 A 錯誤解析
把 float 全部改成 double,只是改變資料的精度大小,跟編譯錯誤的根本原因(f() 函式內用到了 main() 裡才宣告的區域變數 a,b,c,這些名稱在 f() 的作用域中根本不存在)完全無關,改成 double 並不會讓 a,b,c 在 f() 裡變得看得到。
選項 B 錯誤解析
a,b,c 的初始值是不是整數(1、0、-1)跟編譯錯誤完全無關,即使把初始值改成帶小數點的常數(1.0、0.0、-1.0),f() 函式內部依然完全看不到 main() 裡宣告的這些區域變數,問題不在初始值的型態表示方式。
正確答案完整推導(★ 本題標準答案)
a,b,c 是 main() 內的區域變數,而函式 f() 定義在 main() 之外、位置更早,f() 內直接使用 a,b,c,這些名稱在 f() 的作用域中並不存在,編譯器找不到而報錯。解法是把 a,b,c 的宣告移到程式最前面(成為全域變數),這樣 f() 與 main() 都能存取到同一組 a,b,c,選 (C)。
選項 D 錯誤解析
這個選項把問題的方向完全弄反了。題目中的 x,a,b,c 本來就是宣告在 main() 內部的區域變數(並不是全域變數),恰恰是因為它們只是區域變數、f() 看不到,才會編譯錯誤;如果又把它們改宣告成 f() 內的區域變數,main() 反而會看不到它們(main() 裡的 for 迴圈也用到了 x),問題不但沒解決還會製造新的錯誤。
115 統測專二 Q49 📌 概念:遞迴/全域變數/終止條件追蹤
試題來源:技專校院入學測驗中心
C 語言程式呼叫函式對全域變數 count 進行運算,下列程式執行後的輸出結果為何?
題幹程式碼
int count = 0;
void foo(int n){
    if(n != 1){
        ++count;
        foo(n-1);
    }
}
void main(){
    int n = 3;
    foo(n);
    printf("%d", ++count);
}
115 統測專二 Q49 題目截圖
本題標準答案: (A) 3

🔍 四個選項逐項深度剖析

正確答案完整推導(★ 本題標準答案)
foo(3):3!=1為真,count變1,遞迴呼叫foo(2)。foo(2):2!=1為真,count變2,遞迴呼叫foo(1)。foo(1):1!=1為假,終止條件成立,直接返回,不再遞迴、也不會再讓count增加。回到main(),count此時是2,++count是前置遞增,先把count加1變成3,再印出這個新值,選 (A)。
選項 B(4)錯誤解析
此答案可能誤以為 foo(1) 被呼叫時,n!=1 這個條件判斷式仍然成立(誤判1不等於1),因而又多執行了一次 ++count 與遞迴呼叫,讓 count 在 main() 印出前就已經是3,再經過 ++count 後變成4。但 n=1 時 n!=1 確實是假,foo(1) 不會再執行內部的 count 遞增。
選項 C(5)錯誤解析
此答案可能誤以為遞迴會一路呼叫到 foo(0) 甚至更深、每一層都讓 count 遞增,忽略了終止條件是 n!=1(也就是 n 遞減到1時就停止),並非遞減到0才停止,遞迴呼叫的層數被高估了。
選項 D(6)錯誤解析
此答案偏差更大,可能同時疊加了「終止條件判斷錯誤」與「重複計算了main()裡的++count」等多重計算失誤,總計數比正確答案多出了3。核心提醒:遞迴追蹤務必先明確寫下終止條件是在n等於多少時成立,再逐層列出每次遞迴呼叫是否真的執行了count遞增。

9 高職段考與模擬精選實戰題(5 題全選項解析)

透過以下 5 題精選模擬試題,全面檢驗你對全域/區域變數、函式原型、傳值呼叫、遞迴與 static 的掌握度:

模擬實戰第 1 題 📌 概念:全域變數與區域變數的遮蔽(shadowing)
已知下列 C 語言程式碼片段,執行後印出的內容為何?
C Code
int val = 100;
void show() {
    int val = 5;
    printf("%d ", val);
}
int main(void) {
    show();
    printf("%d", val);
    return 0;
}
本題標準答案: (A) 5 100

🔍 四個選項逐項深度剖析

正確答案完整推導(★ 本題標準答案)
show() 函式內宣告了一個同名的區域變數 val=5,這個區域 val 會遮蔽外層的全域 val,show() 內印出的是區域 val 的值5。main() 裡的 printf 直接用的是全域 val,完全沒被 show() 內部的區域變數影響,仍是100。輸出 "5 100",選 (A)。
選項 B(5 5)錯誤解析
此答案誤以為 show() 函式內對區域 val 的賦值,會連帶影響到 main() 裡印出的全域 val,但區域變數與全域變數是兩塊完全獨立的記憶體,show() 內的修改只發生在區域版本身上,不會反映到全域 val。
選項 C(100 100)錯誤解析
此答案誤以為 show() 函式內 int val=5; 這行宣告沒有真的生效,或誤以為 show() 用的其實是全域 val(而非重新宣告的區域 val),忽略了函式內確實重新宣告了一個同名的區域變數,並把它遮蔽了全域版本。
選項 D(105 100)錯誤解析
此答案誤以為 show() 是把5「加」到全域 val 上(100+5=105),但程式碼寫的是 int val = 5;(宣告一個新的區域變數並賦值為5),不是對全域 val 做加法運算,兩者是完全不同的兩塊記憶體,不會有任何加總的效果。
模擬實戰第 2 題 📌 概念:函式原型宣告與呼叫先於定義
已知下列 C 語言程式碼在較嚴謹的編譯設定下可能無法通過編譯,主要原因與正確的修改方式為何?
C Code
#include <stdio.h>
int main(void) {
    printf("%d", square(5));
    return 0;
}
int square(int n) {
    return n * n;
}
本題標準答案: (A) main()呼叫square()時,square()尚未被宣告或定義過,編譯器不知道它的存在;應在main()之前加上函式原型宣告 int square(int);

🔍 四個選項逐項深度剖析

正確答案完整推導(★ 本題標準答案)
main() 裡呼叫 square(5) 時,square() 的完整定義寫在 main() 之後,且沒有事先的函式原型宣告,編譯器在編譯到這個呼叫時還不知道 square() 的參數個數、型態與回傳型態,在較嚴謹的編譯設定下會產生編譯錯誤或警告。最保險的解法是在 main() 之前先加上一行函式原型宣告 int square(int);,讓編譯器提前知道 square() 的簽名,選 (A)。
選項 B 錯誤解析
這個答案完全沒有指出真正的問題——square() 呼叫先於宣告/定義才是編譯疑慮的根本原因,跟參數命名有沒有衝突無關,並不是「完全沒有問題」。
選項 C 錯誤解析
%d 是 C 語言中用來格式化輸出整數的正確寫法,square() 回傳的也確實是 int 型態,這裡的用法完全正確;%s 是用來輸出字串的格式,用在這裡反而是錯的。
選項 D 錯誤解析
C 語言不允許在一個函式的內部定義另一個完整的函式(不支援巢狀函式定義),square() 必須定義在 main() 之外、跟其他函式屬於同一層級,不能宣告在 main() 內部。
模擬實戰第 3 題 📌 概念:傳值呼叫、回傳值與全域變數的綜合追蹤
已知下列 C 語言程式碼片段,執行後印出的內容為何?
C Code
int total = 0;
int addOne(int n) {
    total = total + 1;
    return n + 1;
}
int main(void) {
    int a = 5;
    int b = addOne(a);
    printf("%d %d %d", a, b, total);
    return 0;
}
本題標準答案: (A) 5 6 1

🔍 四個選項逐項深度剖析

正確答案完整推導(★ 本題標準答案)
a 是傳值傳給 addOne() 的 n,函式內完全沒有對 n 做任何修改,a 本身當然維持5。addOne(5) 回傳 n+1=6,指定給 b,b=6。total 是全域變數,addOne() 內直接透過 total=total+1 修改的是全域版本(不是透過參數傳遞),所以 total 確實從0變成1。輸出 "5 6 1",選 (A)。
選項 B(6 6 1)錯誤解析
此答案誤以為 addOne() 內某個運算會回頭修改 main() 的 a,讓 a 也變成6,但 addOne() 的參數 n 是傳值接收,函式內從頭到尾都沒有對 n 或 a 做任何修改的動作,a 應該維持呼叫前的值5。
選項 C(5 5 1)錯誤解析
b 算錯了,此答案誤以為 addOne() 回傳的是 n 本身(也就是5),而不是 n+1 的計算結果6,忽略了函式明確寫著 return n+1;。
選項 D(5 6 0)錯誤解析
a、b 都算對了,但 total 算錯了——此答案誤以為 addOne() 內修改的是「傳進來的一個 total 複本」而非真正的全域變數 total,但 addOne() 並沒有把 total 當作參數傳入,它直接存取、修改的就是全域的 total,這行敘述確實會讓全域 total 從0變成1,不會維持0。
模擬實戰第 4 題 📌 概念:遞迴累加追蹤(終止條件與逐層回帶)
已知下列 C 語言程式碼片段,執行後印出的內容為何?
C Code
int sum(int n) {
    if (n == 0) return 0;
    return n + sum(n - 1);
}
int main(void) {
    printf("%d", sum(4));
    return 0;
}
本題標準答案: (A) 10

🔍 四個選項逐項深度剖析

正確答案完整推導(★ 本題標準答案)
sum(4) 呼叫 sum(3),sum(3) 呼叫 sum(2),sum(2) 呼叫 sum(1),sum(1) 呼叫 sum(0),sum(0) 符合終止條件 n==0,直接回傳0,不再繼續遞迴。逐層往回加總:sum(1)=1+0=1;sum(2)=2+1=3;sum(3)=3+3=6;sum(4)=4+6=10。最終印出10,選 (A)。
選項 B(4)錯誤解析
此答案誤以為 sum() 只是單純回傳參數 n 本身,完全忽略了函式內 return n + sum(n-1); 這行還會把 n 跟「n-1的遞迴結果」加總起來,並不是只回傳 n 這一個數字。
選項 C(24)錯誤解析
這個數字接近4的階乘(4×3×2×1=24),可能誤把「累加」的遞迴(n + sum(n-1))看成了「累乘」的遞迴(n × fact(n-1)),兩種遞迴的寫法很相似但運算完全不同,程式碼裡明確寫的是加號,不是乘號。
選項 D 錯誤解析
這個遞迴函式有明確的終止條件 n==0,且每次呼叫都用 n-1 讓 n 確實在遞減、最終一定會遞減到0,不會無窮遞迴;只有當終止條件寫錯(例如漏寫,或 n 永遠不會等於終止值)時才會發生無窮遞迴。
模擬實戰第 5 題 📌 概念:static 區域變數跨呼叫保留狀態
已知下列 C 語言程式碼片段,執行後印出的內容為何?
C Code
int counter(void) {
    static int c = 10;
    c = c + 5;
    return c;
}
int main(void) {
    printf("%d %d %d", counter(), counter(), counter());
    return 0;
}
本題標準答案: (A) 15 20 25

🔍 四個選項逐項深度剖析

正確答案完整推導(★ 本題標準答案)
static int c = 10; 只會在第一次呼叫 counter() 時初始化一次,之後每次呼叫都會延續上一次結束時的值。第1次呼叫:c=10+5=15;第2次呼叫:c延續上次的15,c=15+5=20;第3次呼叫:c延續上次的20,c=20+5=25。依 printf 引數的呼叫順序輸出 "15 20 25",選 (A)。
選項 B(15 15 15)錯誤解析
此答案誤以為每次呼叫 counter() 時,c 都會被重新初始化成10(就像一般的區域變數那樣),忽略了 static 關鍵字的作用正是讓 c 只在第一次初始化、之後保留上次呼叫結束時的值,不會每次呼叫都重設回10。
選項 C(10 10 10)錯誤解析
此答案誤以為 c=c+5; 這行運算完全沒有生效,或誤以為函式回傳的是 c「修改前」的舊值,忽略了函式明確地先把 c 加5、才把加完之後的新值回傳出去。
選項 D(15 10 15)錯誤解析
第一次呼叫算對了(15),但誤以為第二次呼叫時 c 又被重新初始化回10(跟一般區域變數搞混,忽略了 static 的特性),第三次呼叫又算對了。這種忽高忽低、時對時錯的模式,反映對 static「持續保留狀態」這個特性只是一知半解,並沒有全程正確套用。

10 考前衝刺 10 秒速記口訣 & 核心知識檢核清單

✅
全域 vs 區域
寫在函式外=全域,整個檔案都看得到;寫在函式內=區域,只有那個函式看得到。
✅
遮蔽(shadowing)
區域變數跟全域變數同名時,該範圍內一律指向區域版本,全域版本被暫時擋住看不見。
✅
原型宣告解決呼叫先於定義
函式定義寫在呼叫之後時,要先加一行原型宣告,讓編譯器提早知道函式簽名。
✅
傳值呼叫不會外洩
一般型態參數只是複本,函式內修改參數絕不會影響呼叫端的原始變數。
✅
遞迴要有終止條件
追蹤時先找出終止條件在哪一層觸發,再從最深層開始往回逐層帶回計算結果。
✅
static 只初始化一次
static 區域變數的值會保留到下次呼叫,不會像一般區域變數每次都重設。
✅
函式原型雙重檢查
同時檢查參數型態與回傳型態兩項需求,任一項不符合就不是正確的函式原型。