112 高普考高考三級 資訊處理 程式設計 考古題詳解

112 年・高普考・高考三級・資訊處理
試題代碼 程式設計

第 一 題25 分

關於以下 C 程式碼:

#include <stdio.h> #define SIZE 10 #define THREE 3

unsigned int f1(unsigned int a, unsigned int b) { return (a && !b); }

unsigned int f2(unsigned int a, unsigned int b) { return (a << b); }

unsigned int f3(unsigned int a, unsigned int b) { return (a & ~b); }

int f4(int a, int b) { return a*b+a-b; }

int f5(int a, int b) { int data[SIZE];

for (int i=1, k=0; i<=a; i++) {
    if (i%3==0)
        data[k++]=f4(i, i+1);
}

return data[b];

}

int f6(int a, int b) { int data[][THREE] = {{4,3,2},{3,4,2},{2,3,3}};

for (int i=0; i<THREE; i++) {
    for (int j=0; j<THREE; j++) {
        if (i>a || j>b)
            data[i][j] = data[j][i]+b;
    }
}

return data[a][b];

}

int main() { printf("%u\n", f1(6, 2)); printf("%u\n", f2(6, 2)); printf("%u\n", f3(7, 2)); printf("%d\n", f4(3, 12)); printf("%d\n", f5(15, 3)); printf("%d\n", f5(3, 15)); printf("%d\n", f6(1, 1)); printf("%d\n", f6(3, 2)); return 0; }

請說明程式執行後,程式碼編號 26~33 的輸出,以及其運算邏輯。

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

這一題的完整詳解

核心觀念

本題考查 C 語言的邏輯運算、位元運算、陣列索引與函式呼叫。判讀時要分清楚:

  • &&、! 是邏輯運算,結果為 0 或 1。
  • <<、&、~ 是位元運算。
  • 陣列索引從 0 開始;索引超出陣列範圍會造成未定義行為,不能據此推定固定輸出。
  • 函式內未初始化的區域陣列元素,其值不可當成已知數值使用。

解題方法與逐行推導

依 main 中 printf 的先後順序計算:

  1. f1(6, 2):6、2 都視為真,因此 6 && !2 等同 1 && 0,結果為 0。
  2. f2(6, 2):左移兩位,6≪2=246 \ll 2 = 24。
  3. f3(7, 2):以二進位計算,7=011127=0111_2、2=001022=0010_2;~2 的對應位元中,低三位為 110,所以 0111 & 1101 的低位結果為 0101,即 5。
  4. f4(3, 12):3×12+3−12=273\times12+3-12=27。
  5. f5(15, 3):迴圈將 i=3,6,9,12,15i=3,6,9,12,15 的計算結果依序放入 data[0] 到 data[4]。
🔒

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

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

免費註冊

第 二 題25 分

關於以下 C++ 程式碼:

#include <iostream> #include <string> #include <exception> #include <stdexcept> #include <assert.h>

using namespace std;

class NegativeException: public exception { public: const char * what () const throw () { return "negative"; } };

class DivideByZeroException: public logic_error { public: DivideByZeroException() : logic_error("divide by zero") {} };

int getResult(int x, int y) { if (x<0 || y<0) throw NegativeException(); else if (y==0) throw DivideByZeroException();

return (x/y);

}

void f(int x, int y) { try { cout << "Result:" << getResult(x, y) << endl; } catch (std::exception& e) { cout << "!:" << e.what() << "\n"; } }

void testResult() { f(2, -1); f(2, 0); f(2, 3); f(6, 3); }

void assertResult() { assert(getResult(8, 4)==1); }

int main() { testResult(); assertResult(); return 0; }

(一)請說明程式執行後的輸出。

(二)請說明程式中 assert 與 exception 的使用時機與目的。

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

這一題的完整詳解

核心觀念

本題考查 C++ 的例外處理與 assert 斷言:

  • throw 會中斷目前函式的正常執行,將例外交給呼叫端的 catch 處理。
  • catch (std::exception& e) 可捕捉公開繼承自 std::exception 的例外物件,並用 e.what 取得錯誤訊息。
  • assert(條件) 用於程式開發期間檢查條件是否成立;條件不成立時會中止程式。若以 NDEBUG 編譯,assert 會被停用。

解題方法

依序追蹤 testResult 中的四次呼叫,判斷 getResult(x, y) 是否回傳整數或拋出例外,再看 f 的 try-catch 如何處理。最後檢查 assertResult 的條件。

  1. f(2, -1):x < 0 || y < 0 成立,getResult 拋出 NegativeException。f 捕捉例外並輸出 !: 加上 what 回傳的 "negative"。由於例外在 cout << "Result:" << getResult(...) 中發生,getResult 尚未回傳,故 "Result:" 不會輸出。
  2. f(2, 0):負數條件不成立,y == 0 成立,拋出 DivideByZeroException。此類別公開繼承 logic_error,而 logic_error 又繼承自 std::exception,因此會被同一個 catch 捕捉,輸出其訊息 "divide by zero"。
  3. f(2, 3):沒有例外,整數除法 2/32 / 3 的結果為 00,輸出 Result:0。
  4. f(6, 3):沒有例外,6/3=26 / 3 = 2,輸出 Result:2。
  5. assertResult:呼叫 getResult(8, 4) 得到 22,但斷言檢查的是 2 == 1,條件為假。在未定義 NDEBUG 的一般除錯編譯下,assert 會輸出失敗資訊並中止程式,因此 main 不會正常執行到 return 0。斷言失敗訊息的檔名、行號及格式依編譯器與執行環境而異。

(一)程式輸出

🔒

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

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

免費註冊

第 三 題25 分

關於以下 Java 程式:

import java.io.*; import java.util.ArrayList;

abstract class Fruit { public Fruit(int sweetness) { this.sweetness = sweetness; }

public abstract String eat();

protected String taste() {
    if (sweetness>0 && sweetness<5)
        return "no";
    else if (sweetness<=10)
        return "little";
    else if (sweetness<=15)
        return "some";
    else if (sweetness<=20)
        return "more";
    else
        return "super";
}

private int sweetness;

}

class Apple extends Fruit { public Apple(String c, int s) { super(s); this.color = c; }

public String eat() {
    return color + ":" + taste() + " sweetness ";
}

private String color;

}

class Watermelon extends Fruit { public Watermelon(String v, int s) { super(s); this.volume = v; }

public String eat() {
    return volume + ":" + taste() + " sweetness ";
}

private String volume;

}

public class Test { public static void test01() { ArrayList<Fruit> fs = new ArrayList<Fruit>(); fs.add(new Apple("red", 18)); fs.add(new Watermelon("big", 20)); fs.add(new Apple("green", 10));

    fs.forEach((n) -> System.out.println(n.eat()));
}

public static void main(String[] args) throws InterruptedException {
    test01();
}

}

(一)請說明程式執行後其輸出與其運作程式碼行數順序。

(二)請依下面表格,說明 Fruit 的設計功用,包含 Fruit 類別類型與功用以及方法(method)。

說明Fruit 類別類型與功用Fruit 方法功用eat 功用taste 功用

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

這一題的完整詳解

核心觀念

本題考查 Java 的抽象類別、繼承、方法覆寫、多型與 Lambda 運算式。

  • Fruit 是抽象類別,不能直接建立物件;它提供子類別共用的資料與方法,也要求子類別實作抽象方法 eat。
  • Apple、Watermelon 繼承 Fruit,透過 super(s) 呼叫父類別建構子,並覆寫 eat。
  • ArrayList<Fruit> 可以存放各種 Fruit 子類別物件。呼叫 n.eat 時,Java 依照物件的實際型別,執行該物件覆寫的 eat,這就是多型。
  • taste 依 sweetness 的數值區間回傳甜度描述。

解題方法

先依照 test01 中的加入順序,逐一確認物件型別與甜度,再套用 taste 的判斷條件:

  1. Apple("red", 18):sweetness 為 18,符合 sweetness<=20,回傳 "more"。
  2. Watermelon("big", 20):sweetness 為 20,符合 sweetness<=20,回傳 "more"。
  3. Apple("green", 10):sweetness 為 10,不符合前面的 sweetness>0 && sweetness<5,但符合 sweetness<=10,回傳 "little"。

各子類別的 eat 都將自身資料、冒號、taste 結果與 " sweetness " 串接,因此輸出依序為:

red:more sweetness
big:more sweetness
green:little sweetness

(一)輸出與運作程式碼行數順序

程式由 main 呼叫 test01。集合依序加入三個物件;forEach 再依集合順序處理每個物件,並呼叫其實際型別所提供的 eat。

執行順序運作程式碼說明
1main 中的 test01;呼叫測試方法。
2ArrayList<Fruit> fs = new ArrayList<Fruit>;建立可存放 Fruit 子類別物件的清單。
3fs.add(new Apple("red", 18));建立 Apple,呼叫 Fruit 建構子設定甜度,再設定顏色並加入清單。
4fs.add(new Watermelon("big", 20));建立 Watermelon,呼叫 Fruit 建構子設定甜度,再設定體積並加入清單。
5fs.add(new Apple("green", 10));建立第二個 Apple,設定甜度與顏色後加入清單。
🔒

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

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

免費註冊

第 四 題25 分

以下 C++ 程式有部分違反安全程式設計原則,可能具有許多潛在風險。

#include <iostream> #include <string> #define SIZE 10 using namespace std;

class Food { public: Food() = default;

Food(int c) {
    cal = c;
}

int getCal() {
    return cal;
}

private: int cal; };

void f1() { Food *f[SIZE]; cout << f[0]->getCal() << endl; }

void f2(int n) { string *f = NULL;

for(int i = 0; i < n; i++)
    f = new string("ok");

cout << *f << endl;

}

void f3(int n) { double x = 3, y1 = 5, y2 = 2;

for (int i=0; i<n; i++) {
    x = x/10.0;
    y1 = y1/10.0;
    y2 = y2/10.0;
}

if(x == (y1-y2))
    cout << "X == Y" << endl;

}

void f4(char *s1, char *s2) { int len = 0; char *s = s1;

while (*s2 != '\0') {
    *s1 = *s2;
    s1++;
    s2++;
}

cout << s << endl;

}

void f5(int n) { int result = 0; int *d = new int[n];

d[0] = d[1] = 1;

for(int i = 0; i < n-2; i++)
    d[i+2] = d[i+1]+d[i];

for(int i = 0; i < n; i++)
    result = result + d[i];

cout << result << endl;

}

int main() { char s1[] = "goodness", s2[] = "food";

f1();
f2(2);
f3(1);
f4(s1, s2);
f5(6);

return 0;

}

請說明此程式,執行函式 f1()~f5() 的輸出,以及函式 f1()~f5() 可能具有的潛在風險。

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

這一題的完整詳解

核心觀念

本題考查 C++ 的指標與動態記憶體、物件初始化、陣列邊界,以及浮點數比較。程式中有些操作會造成未定義行為;一旦發生,C++ 標準不保證程式輸出或後續執行結果。因此,下列輸出須區分「可依程式推得的結果」和「因未定義行為而無法保證的結果」。

解題方法

依序追蹤每個函式的變數初值、記憶體操作與迴圈更新,再檢查指標是否指向有效物件、陣列索引是否在範圍內,以及輸出前是否已寫入字串結尾字元 '\0'。

f1:未初始化的指標陣列

Food *f[SIZE];
cout << f[0]->getCal << endl;

f 是含有 10 個 Food* 的區域陣列。宣告時沒有初始化,因此 f[0] 的值不確定;透過它呼叫 getCal 會使用無效指標,造成未定義行為。

  • **輸出:**無法保證,可能沒有任何輸出,也可能異常終止或出現其他結果。
  • **風險:**未初始化指標解參考。
  • **安全作法:**先讓指標指向有效的 Food 物件,或將不使用的指標初始化為 nullptr,並在使用前檢查。

f2:重複配置而未釋放

string *f = NULL;

for(int i = 0; i < n; i++)
 f = new string("ok");

cout << *f << endl;

n = 2 時,迴圈配置兩個 string。第二次配置後,f 改為指向第二個字串;第一個配置物件的位址已遺失,無法釋放,造成記憶體洩漏。最後解參考 f 時仍指向有效字串 "ok"。

  • 輸出:ok
  • **風險:**每次迴圈都配置新物件,卻沒有釋放舊物件;本題執行兩次配置,洩漏第一個物件。函式結束也沒有釋放第二個物件。
  • **安全作法:**若只需一個字串,直接使用區域變數;若確實需要動態配置,使用 std::unique_ptr 等 RAII 管理方式。

f3:浮點數精確比較

n = 1,迴圈執行一次:

x=310=0.3,y1=510=0.5,y2=210=0.2x = \frac{3}{10} = 0.3,\quad y_1 = \frac{5}{10} = 0.5,\quad y_2 = \frac{2}{10} = 0.2

數學上 y1−y2=0.3y_1-y_2=0.3,但二進位浮點格式通常無法精確表示 0.30.3、0.50.5、0.20.2。計算 y1-y2 的結果不保證與 x 的浮點表示完全相同,因此 x == (y1-y2) 不可靠;在常見實作中條件不成立。

  • **輸出:**常見實作下不輸出任何文字;標準不保證此精確比較必定為假。
  • **風險:**用 == 比較浮點數,容易因表示與運算誤差而產生非預期結果。
  • **安全作法:**依問題所需精度,以容許誤差比較,例如判斷 ∣x−(y1−y2)∣<ε|x-(y_1-y_2)|<\varepsilon。

f4:複製字元時未補字串結尾

🔒

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

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

免費註冊

其他考古題