109 高普考高考三級 資訊處理 程式語言 考古題詳解
第 一 題25 分
請將下面程式改寫為尾遞迴(tail recursion)的形式。(15 分)
int recsum(int x){ if (x == 1) return(x); return(x + recsum(x - 1)); }
請問尾遞迴形式的優點為何?(10 分)
登入後即可作答並保存紀錄。
核心觀念
原程式在遞迴呼叫返回後,還要執行加法:
return(x + recsum(x - 1));
因此它是非尾遞迴。尾遞迴的條件是:遞迴呼叫必須是函式最後一個動作,呼叫返回後不再有其他運算。
要把尚未完成的加法工作放進累積參數,讓每次遞迴呼叫都帶著目前的累計值前進。
解題方法
原函式計算的是:
新增累積參數 sum,用來保存已累加的部分。每次將目前的 x 加進 sum,再以 x - 1 繼續遞迴;當 x == 1 時,回傳最後的總和。
int recsum_tail(int x, int sum) {
if (x == 1)
return sum + x;
return recsum_tail(x - 1, sum + x);
}
int recsum(int x) {
return recsum_tail(x, 0);
}
以 x = 4 為例,呼叫參數依序為:
最後回傳 ,與原程式的 相同。遞迴呼叫 recsum_tail(x - 1, sum + x) 是最後一個動作,因此符合尾遞迴定義。
尾遞迴形式的優點
第 二 題25 分
假設每個 int 變數占用 4 bytes,每個指標變數也占用 4 bytes。下面的 C 程式印出的結果為何?
#include <stdio.h>
typedef int T1[10][9];
int main(){ struct { T1 *a[10]; int (*b)[100]; } f[10][10][10];
printf("p1 = %d\n", (int)sizeof(f[1][5]));
printf("p2 = %d\n", (int)sizeof(f[2][3][4].a));
printf("p3 = %d\n", (int)sizeof(f[3][2][6].b));
printf("p4 = %d\n", (int)(f - &f[5]));
printf("p5 = %d\n", (int)(f[6][2] - f[3][3]));
}
登入後即可作答並保存紀錄。
核心觀念
本題考查 C 語言的多維陣列、指標與陣列的型別,以及 sizeof 和指標相減的單位。
typedef int T1[10][9];
因此,T1 是含有 個 int 的二維陣列,大小為
結構中的 a 是由 10 個「指向 T1 的指標」組成的陣列;b 是指向含有 100 個 int 之陣列的指標。兩者都是指標,大小均為 4 bytes。
f 的型別是三維陣列,其每個元素都是該匿名結構。指標相減的結果則是兩個指標之間相隔的「元素個數」,不是位元組數。
解題方法
先由內而外判讀宣告,再逐一套用陣列大小與指標運算規則。
結構大小為
本題假設 int 與指標各占 4 bytes,且結構成員的排列不需額外填補。
f 的維度為 [10][10][10],所以:
f[i]是一個[10][10]的陣列。f[i][j]是一個[10]的陣列。f[i][j][k]是一個結構元素。
逐項計算
p1
sizeof(f[1][5])
f[1][5] 是長度 10 的結構陣列,因此
所以 p1 = 440。
p2
sizeof(f[2][3][4].a)
.a 是含有 10 個指標的陣列,每個指標占 4 bytes:
第 三 題25 分
下面的 C 程式印出的結果為何?作答必須解釋計算過程,只寫答案而未加解釋,只能得部分分數。
#include <stdio.h>
int foo1(int p){ if (p >= 90) return(foo1(foo1(p - 11))); return(p + 10); }
int foo2(int p){ if (p < 91) return(foo2(p + 11)); return(p); }
int foo(int p){ return(foo1(foo2(p))); }
int main(int argc, char **argv){ int q;
q = 65;
printf("foo(%d)= %d.\n", q, foo(q));
q = 83;
printf("foo(%d)= %d.\n", q, foo(q));
q = 95;
printf("foo(%d)= %d.\n", q, foo(q));
q = 100;
printf("foo(%d)= %d.\n", q, foo(q));
q = 142;
printf("foo(%d)= %d.\n", q, foo(q));
return(0);
}
登入後即可作答並保存紀錄。
核心觀念
本題考遞迴函式的呼叫順序與回傳值。計算時要先求出內層函式呼叫的回傳值,再將結果代入外層函式。
foo2(p):當 時,遞迴呼叫foo2(p + 11);直到參數至少為 ,才回傳該參數。foo1(p):當 時,先計算foo1(p - 11),再把該回傳值當作參數呼叫另一層foo1;當 時,回傳 。foo(p):先計算foo2(p),再把結果傳入foo1,即foo1(foo2(p))。
解題方法
先整理 foo2 的作用:它每次將小於 的參數加 ,直到參數達到或超過 ,再回傳參數。因此:
| 輸入 | foo2(p) 的遞迴參數 | 回傳值 |
|---|---|---|
| 65 | 98 | |
| 83 | 94 | |
| 95 | 95 | |
| 100 | 100 | |
| 142 | 142 |
接著計算 foo1。當輸入至少為 ,程式會先對 呼叫 foo1,再以該次回傳值作為新的參數呼叫 foo1。
-
foo1(98)所以foo1(98)=95。 -
foo1(94)
第 四 題25 分
物件導向程式語言有繼承的觀念,請解釋單一繼承(single inheritance)與多重繼承(multiple inheritance)的意義、差別及實作方法。
登入後即可作答並保存紀錄。
核心觀念
本題考查物件導向程式語言中的繼承:子類別(subclass)從父類別(superclass)取得資料欄位與方法,並可在此基礎上新增或覆寫功能。繼承能重用程式碼,也能表達類別間的「是一種」(is-a)關係。
解題方法
可從「一個子類別可直接繼承幾個父類別」切入,再比較類別關係與實作方式。
- 單一繼承(single inheritance):每個子類別至多直接繼承一個父類別。繼承關係形成樹狀結構;子類別仍可透過父類別的父類別,間接取得更上層的功能。
- 多重繼承(multiple inheritance):一個子類別可直接繼承兩個或以上的父類別。子類別可以同時重用多個類別的屬性與方法,類別關係較具彈性,但也會增加名稱衝突與繼承歧異的處理難度。
例如,Student 可單一繼承 Person;若語言支援多重繼承,也可讓 TeachingAssistant 同時繼承 Student 與 Employee,取得兩者的功能。
實作方法與差異
以類別宣告來看,單一繼承只指定一個父類別;多重繼承則指定多個父類別。以下以類似 C++ 的語法示意: