115 身心障礙特考四等 資訊處理 程式設計概要 考古題詳解

115 年・身心障礙特考・四等・資訊處理
試題代碼 程式設計概要

第 一 題25 分

請根據下方 C 語言的程式碼片段,回答下列問題:

// 方法(A):巨集定義
#define CALC(x, y) ( x * y + x)

// 方法(B):函式定義
int calc(int x, int y) { return x * y + x; }

int main() { int a = 2, b = 3;

// 執行方法(A)
int resultA = CALC(++a, b + 2);

a = 2;  // 重置 a 的值進行方法(B)
int resultB = calc(++a, b + 2);

return 0;

}

(一)請寫出 CALC(++a, b+2)CALC(++a,\ b+2) 巨集展開後的實際運算式,並據此計算 resultAresultA 的值。(5 分)

(二)請問方法(A)以及方法(B)在參數處理方式及實際執行結果上的差異。(10 分)

(三)若將方法(A)修正為:

#define CALC(x, y) ((x) * (y) + (x))

以便解決運算優先順序之問題,請說明是否可以得到與方法(B)相同的結果?(10 分)

登入後即可作答並保存紀錄。

這一題的完整詳解

核心觀念

本題考查 C 語言的前置處理器巨集展開與函式呼叫的差異:

  • 巨集在編譯前進行文字替換;引數代入後,運算式依 C 語言的運算子優先順序及結合方向求值。
  • 函式呼叫會先求出各引數的值,再將值傳入函式。此例中,函式內的 x、y 是各自的區域參數。
  • 加上括號可以避免巨集引數代入後發生優先順序錯誤,但不會讓巨集只求值一次,也不會消除重複使用引數的副作用。

解題方法

(一)巨集展開與 resultA

巨集定義為:

#define CALC(x, y) ( x * y + x)

將 ++a 代入 x,將 b + 2 代入 y,得到:

( ++a * b + 2 + ++a)

注意,巨集本體中的 y 前面沒有括號,因此 b + 2 代入後,乘法只會先乘上 b;整個式子等同於:

((++a * b) + 2 + ++a)

在 resultA 這行執行前,a=2a=2、b=3b=3。但這個運算式在同一個完整運算式中對 a 做了兩次未排序的修改:一次是第一個 ++a,另一次是最後一個 ++a。C 語言不保證這兩次修改的先後順序;依 C 語言標準,此處行為未定義(undefined behavior)。

因此,不能由標準 C 語言規則得出唯一的 resultA 數值。不能把兩次遞增擅自排成先後次序,再據此給出確定數值。

(二)方法(A)與方法(B)的差異

方法(A)是前處理器巨集,呼叫時直接以文字替換引數。由於 x 在巨集本體中出現兩次,++a 也會出現兩次,造成同一完整運算式內重複修改 a,因此行為未定義;

🔒

後續完整解題步驟與【答案】

免費註冊,享 3 天全站完整詳解閱覽。

免費註冊

第 二 題25 分

請根據下方 Java 程式碼,回答下列問題:

class Main { public static void main(String[] args) { int[] myArr = {10, 20}; modify(myArr);

    for (int i : myArr) {
        System.out.print(i + " ");
    }
}

public static void modify(int[] arr) {
    arr[0] = 100;              // 操作 A
    arr = new int[]{1, 2, 3};  // 操作 B
    arr[0] = 999;              // 操作 C
}

}

(一)請比較一般程式語言中函式參數傳遞機制,傳值(Pass by value)與傳參考(Pass by reference)的差異。(5 分)

(二)請寫出上方程式碼執行後輸出的結果。(5 分)

(三)請依據上方程式碼的輸出結果,說明 Java 在傳遞陣列參數之機制,其本質是採用傳值還是傳參考?請結合操作 B 以及其後續的指令,解釋為何輸出結果符合該機制。(15 分)

登入後即可作答並保存紀錄。

這一題的完整詳解

核心觀念

本題考查函式參數的傳遞方式,以及 Java 傳遞陣列參數時的行為。

  • 傳值(Pass by value):函式取得引數值的副本。函式內重新指定參數,只會改變該副本,不會改變呼叫端的變數。
  • 傳參考(Pass by reference):函式直接取得呼叫端變數的參考;函式內重新指定參數,會使呼叫端變數也指向新的對象。

Java 一律採用傳值。傳入基本型別時,複製的是基本型別的值;傳入物件或陣列時,複製的是物件參考的值。因此,呼叫端與函式中的參數可以透過參考值指向同一個陣列,但函式重新指定參數,不會改變呼叫端的陣列參考。

解題方法

依序追蹤呼叫端陣列,以及 modify 方法內參數 arr 所指向的陣列:

  1. 初始狀態:myArr 指向內容為 [10, 20] 的陣列;呼叫 modify(myArr) 時,arr 取得該參考值的副本,因此兩者暫時指向同一陣列。
  2. 操作 A:arr[0] = 100:透過 arr 修改共同指向的陣列,陣列內容變成 [100, 20]。因此 myArr 所指的內容也已改變。
  3. 操作 B:arr = new int[]{1, 2, 3}:建立新陣列,並將方法內的區域參數 arr 改為指向新陣列。
🔒

後續完整解題步驟與【答案】

免費註冊,享 3 天全站完整詳解閱覽。

免費註冊

第 三 題25 分

請根據下方的 Java 程式碼,回答下列問題:

class Animal { void eat() { System.out.print("Generic.."); } }

class Bird extends Animal { void eat() { super.eat(); System.out.print("Using beak(喙)"); }

void fly() {
    System.out.println("I can fly");
}

}

(一)請說明過載(Overloading)以及覆寫(Overriding)在 Java 程式語言上的差別。(3 分)

(二)上方程式碼展現了那種特性(Overloading 或是 Overriding)?(2 分)

(三)請寫出執行 new Bird().eat()new\ Bird().eat() 的輸出結果。(5 分)

(四)根據上方程式碼,Bird 類別具備飛行 fly()fly() 的方法,當要新增一個 Ostrich(鴕鳥,屬於鳥類但卻不會飛行)類別,若採用直接繼承 Bird 類別會遇到什麼問題?(5 分)

(五)根據(四)所面臨的問題,請提出兩種解決該問題的方法。(10 分)

登入後即可作答並保存紀錄。

這一題的完整詳解

核心觀念

本題考查 Java 的方法過載(Overloading)、方法覆寫(Overriding),以及物件導向設計中的繼承與「不適當繼承」問題。

  • 過載:同一個類別中有多個同名方法,但參數列不同,例如參數個數、型別或順序不同。呼叫哪個方法,主要依編譯時期的參數型態決定。
  • 覆寫:子類別重新提供父類別既有方法的實作。方法名稱與參數列須相同,回傳型別須相同或符合 Java 的協變回傳型別規則,且存取權限不可縮小。呼叫實例方法時,會依執行時期物件的實際類別決定執行哪個實作。
  • 繼承設計問題:子類別若繼承了不符合自身能力或行為的方法,會造成類別模型不合理,並違反里氏替換原則(Liskov Substitution Principle, LSP)的精神。

解題方法

(一)過載與覆寫的差別

比較項目過載 Overloading覆寫 Overriding
發生位置通常在同一類別中父類別與子類別之間
方法名稱相同相同
參數列必須不同必須相同
判定時機編譯時期依參數列選擇執行時期依實際物件型別分派
與繼承關係不一定需要繼承需要有繼承或介面實作關係

**注意:**只改變回傳型別而不改變參數列,不能構成方法過載;子類別若寫出相同名稱但不同參數的方法,屬於過載,不是覆寫。

(二)程式碼展現的特性

Animal 宣告了 eat,而 Bird extends Animal 又宣告了相同名稱、相同參數列的 eat,因此 Bird.eat 覆寫了 Animal.eat。

super.eat 是在子類別覆寫方法中明確呼叫父類別的 eat 實作,不會因此變成過載。

(三)new Bird.eat 的輸出

依序追蹤方法執行:

  1. 呼叫 Bird 物件的 eat。
  2. super.eat 執行 Animal.eat,輸出 Generic..。
  3. 接著執行 Bird.eat 中的第二個 System.out.print,輸出 Using beak(喙)。
  4. 兩處都使用 print,不會自動換行。

因此輸出為:

Generic..Using beak(喙)

(四)直接讓 Ostrich 繼承 Bird 的問題

Bird 提供 fly 方法。若 Ostrich extends Bird,鴕鳥便會繼承 fly,即使鴕鳥不會飛,程式介面仍允許呼叫 new Ostrich.fly。

若鴕鳥不支援飛行,繼承來的方法就不符合鴕鳥的實際行為;若將 fly 覆寫成空方法或丟出例外,則又使呼叫端無法一致地依賴 Bird 的飛行能力。這是父類別把「鳥」與「會飛」綁在一起所造成的設計缺陷,也違反 LSP 的設計精神。

(五)兩種解決方法

方法一:將共同行為抽到較一般的父類別,另以飛行介面表示飛行能力

讓 Bird 僅代表鳥類的共通特徵,將 fly 移至介面,例如:

🔒

後續完整解題步驟與【答案】

免費註冊,享 3 天全站完整詳解閱覽。

免費註冊

第 四 題25 分

下方 Java 程式碼中,process()process() 方法根據支付方式之字串會執行不同邏輯。此設計的問題是:每新增一種支付方式,都必須修改 process()process() 方法。為了解決這個問題,可以透過繼承與多型的方式提高程式維護性。請使用 Java 的繼承與多型重構此程式,並完成下列要求:

public class PaymentService { public void process(String type, double amount) { if (type.equals("CreditCard")) { System.out.println("Processing Credit Card: " + amount); } else if (type.equals("PayPal")) { System.out.println("Processing PayPal: " + amount); }

    // 未來若增加 TaiwanPay,必須在此繼續修改代碼
}

}

(一)定義一個抽象類別 Payment。(5 分)

(二)將不同支付方式(包含新增之 TaiwanPay)的處理邏輯移至對應子類別。(15 分)

(三)寫出重構後呼叫端使用方式。(5 分)

登入後即可作答並保存紀錄。

這一題的完整詳解

核心觀念

本題考查 Java 的抽象類別、繼承與多型,以及如何將依支付方式判斷的條件分支改為由各支付類別分別處理。

原程式使用 if-else 判斷支付方式;新增 TaiwanPay 時,必須修改 PaymentService.process。重構後,PaymentService 只接收抽象型別 Payment,並呼叫其 process 方法;實際執行哪種支付邏輯,由傳入物件的執行期型別決定。

解題方法

先定義抽象類別 Payment,宣告共同操作 process(double amount)。再讓信用卡、PayPal 與 TaiwanPay 類別繼承 Payment,各自覆寫處理方法。最後,讓 PaymentService 接收 Payment 參數並呼叫方法,不再判斷支付方式字串。

abstract class Payment {
 public abstract void process(double amount);
}

抽象類別可作為共同型別,並要求子類別實作抽象方法。由於 Payment 無法直接建立物件,呼叫端須傳入具體子類別的物件。

子類別實作

🔒

後續完整解題步驟與【答案】

免費註冊,享 3 天全站完整詳解閱覽。

免費註冊

其他考古題

資訊處理《程式設計概要》其他年度