111 高普考高考三級 資訊處理 程式設計 考古題詳解
第 一、 題20 分
請問以下三小題 C 程式執行的結果為何?請注意須說明答案是如何產生的,否則不給分。
(一)(5 分)
#include <stdio.h> #include <stdlib.h>
int main(void) { int x; float y;
for (x=0, y=50; x<25; x+=5, y/=2)
printf("x=%d, y=%4.2f\n", x, y);
return 0;
}
(二)(5 分)
#include <stdio.h> #include <stdlib.h>
int a=10, fun(int);
int main(void) { int b=6;
printf("a=%d, b=%d, fun(a)=%d\n", a, b, fun(a));
return 0;
}
int fun(int b) { a-=5; b/=2; return(a+b); }
(三)(10 分)
#include <stdio.h> #include <stdlib.h>
#define SIZE 10
void fun(int *, int);
int main(void) { int x[SIZE] = {1,2,3,4,5,6,7,8,9,10};
fun(x, SIZE);
printf("\n");
return 0;
}
void fun(int a, int size) { if (size > 0) { fun(a+3, size-3); printf("(a+%d)=%d\n", SIZE-size, *a); } }
登入後即可作答並保存紀錄。
核心觀念
for迴圈的多重運算式控制:在for的初始化與迭代步進區塊中,使用逗號運算子(Comma Operator,)可同時對多個變數進行賦值與更新。- 格式化輸出(
printf):
%d:以十進位整數格式輸出。%4.2f:浮點數格式化輸出,其中小數點後保留 位,總欄位寬度至少為 。當數值總長度(含整數、小數點及小數位數)大於等於寬度規範時,系統依實際長度完整顯示,不予截斷。
- 終止條件判斷:每次迴圈本體執行完畢後,執行步進運算式,再判定條件式是否成立,決定是否繼續執行。
解題方法
初始狀態:(整數),(單精度浮點數)。 每次迴圈結束後執行更新運算:,。
執行追蹤(Trace Table):
| 迴圈次數 | 條件檢查() | 執行輸出(printf("x=%d, y=%4.2f\n", x, y);) | 步進更新() |
|---|---|---|---|
| 第 1 次 | (成立) | x=0, y=50.00 | |
| 第 2 次 | (成立) | x=5, y=25.00 | |
| 第 3 次 | (成立) | x=10, y=12.50 | |
| 第 4 次 | (成立) | x=15, y=6.25 | |
| 第 5 次 | (成立) | x=20, y=3.13(或 3.12) | |
| 第 6 次 | (不成立) | 終止迴圈,跳出 | 結束 |
解題技巧與常見陷阱
- 欄位寬度與位數:
%4.2f的寬度 包含整數部、小數點與 位小數。例如 總長度為 個字元,超過設定寬度 ,因此輸出不會補空格,直接輸出50.00。 - 浮點數捨入規則:當 時,依一般數學四捨五入輸出為
3.13;若依部分符合 IEEE 754 規範之編譯器(採偶數捨入法 / Banker's Rounding),因前一位為偶數 ,亦可能輸出為3.12。國考閱卷時此兩種捨入值皆屬標準給分範圍。 - 終止邊界:判斷式為 (不含等號),因此 時不會進入迴圈本體。
【答案】
x=0, y=50.00
x=5, y=25.00
x=10, y=12.50
x=15, y=6.25
x=20, y=3.13
(註:最後一行之 y 依編譯器捨入模式亦可為 3.12)
(二)
核心觀念
- 變數有效範圍(Scope)與遮蔽(Shadowing):
- 檔案層級宣告的變數為全域變數(Global Variable),在程式各函式中皆可存取。
- 區域變數(Local Variable)與函式參數的有效範圍僅限於宣告所在的區塊,若與全域變數同名,會遮蔽全域變數。
- 傳值呼叫(Call by Value):C 語言之引數傳遞皆為傳值呼叫。函式內的參數為獨立記憶體空間的複本,對其運算不影響呼叫端的原始變數。
- 副作用(Side Effect)與引數評估順序(Order of Evaluation):
- 在 ISO C 標準中,函式呼叫時引數的計算順序屬於「未指定行為」(Unspecified Behavior)。
- 主流編譯器(如 GCC、Clang、MSVC 於 x86/x64 環境下之
cdecl呼叫慣例)通常採「由右至左」(Right-to-Left)順序評估引數。
解題方法
- 變數識別:
- 全域變數:
int a = 10; main函式區域變數:int b = 6;
- 函式
fun(int b)執行推導:
- 呼叫
fun(a)時,傳入當時全域變數的值 。 - 函式參數
b接收到 (與main內的b毫無關聯)。 a -= 5;:修改全域變數 ,使全域變數 。b /= 2;:修改參數b,使參數 。return(a+b);:回傳 。
printf("a=%d, b=%d, fun(a)=%d\n", a, b, fun(a));之引數求值分析:
- 情況一:主流編譯器慣例(由右至左評估)
- 先求值最右側引數
fun(a):傳入 ,全域變數 被修改為 ,函式回傳 。 - 接著求值中間引數
b:取main區域變數 。 - 最後求值最左側引數
a:取全域變數 ,此時已被fun修改為 。 - 輸出結果為:
a=5, b=6, fun(a)=10。
- 情況二:由左至右評估
第 二、 題20 分
資料庫是企業組織或政府部門中用以保存大量資料的機制。下圖為資料表的結構,請回答以下各小題。(每小題 10 分,共 20 分)
🖼️【此處有附圖,請對照原卷】
資料表結構如下:
employee:
- id:varchar(10)
- first_name:varchar(30)
- last_name:varchar(20)
salary_slip:
- id:varchar(10)
- year:int
- month:int
- amount:int
employee 與 salary_slip 的關係為一對多,即一位 employee 可對應多筆 salary_slip。
(一)請以 SQL 列出某一個人的年度薪資總額,必須顯示 id、first_name、last_name 及總額。
(二)現在的資料查詢服務常見於網頁系統,而 SQL Injection 是最常見的資料庫攻擊方式。請問下列查詢語句是否會有被攻擊的疑慮?若有的話該如何改善?程式碼可以使用 PHP 或 ASP.NET。
SELECT first_name, last_name FROM employee WHERE id='A001'
登入後即可作答並保存紀錄。
核心觀念: salary_slip 每筆資料代表一筆薪資紀錄;要計算年度總額,需依員工與年度篩選資料,再以 SUM(amount) 加總。因題目未指定年度,以下以 :year 作為年度參數、:id 作為員工編號參數。
解題方法: 以 employee.id = salary_slip.id 連結兩表,再依員工編號篩選並分組。使用 LEFT JOIN 可保留沒有該年度薪資紀錄的員工,並以 COALESCE 將加總結果顯示為 0。
SELECT e.id,
e.first_name,
e.last_name,
COALESCE(SUM(s.amount), 0) AS annual_total
FROM employee AS e
LEFT JOIN salary_slip AS s
ON s.id = e.id
AND s.year = :year
WHERE e.id = :id
GROUP BY e.id, e.first_name, e.last_name;
解題技巧: 年度條件應放在 LEFT JOIN 的 ON 子句中;若放在 WHERE 子句,沒有薪資紀錄的員工會被排除,效果會近似 INNER JOIN。
(二)SQL Injection 判斷與改善
核心觀念: SQL Injection 是將使用者輸入直接拼接進 SQL 指令,使輸入內容被當成 SQL 語法執行。使用參數化查詢可將輸入值與 SQL 指令分開處理。
題目中的查詢為:
第 三、 題30 分
請使用 C、C++、Java 或 Python 程式語言撰寫採用五位數整數來進行資料加密的完整程式。(30 分)
- 撰寫加密函數 encrypt(),此函數可將所傳入的五位數整數進行加密,並將加密後的密碼傳回。加密的規則如下:
- 先將該五位數整數的每位數值分別以「加 5 後除以 10 之餘數」取代。
- 再將取代後之數字的第一位數與第五位數互換,第二位數與第四位數互換,第三位數不變,最後取得一個新整數,此即為加密後的密碼。(註:第一位數為萬分位、第二位數為千分位,以此類推。)
-
撰寫解密函數 decipher(),此函數可將所傳入的五位數密碼進行解密,並將解密後的整數傳回。解密的規則是將上述加密後之密碼還原為原傳入的整數。
-
撰寫主程式來驗證上述之加密與解密函數的正確性。程式一開始先讓使用者輸入任一之五位數整數[不在合理範圍內(10000~99999)須請使用者重新輸入],在呼叫加密函數後將所傳回之密碼從螢幕上顯示出;再將該密碼傳入解密函數,再將所傳回之整數從螢幕上顯示出。程式須可讓使用者持續輸入五位數之整數,直到使用者輸入 0(整數)時結束程式的執行。
以下為程式執行的範例:(備註:斜體加外框線之整數為使用者所輸入,其餘皆為程式執行的輸出。)
請輸入一個五位數的整數(輸入 0 結束程式):1234 所輸入的數值並不是五位數之整數 請重新輸入:12345 加密後的密碼為:09876 解密後的密碼為:12345 請輸入一個五位數的整數(輸入 0 結束程式):67890 加密後的密碼為:54321 解密後的密碼為:67890 請輸入一個五位數的整數(輸入 0 結束程式):0 感謝使用此程式,歡迎您對此程式的優化提出建議。
登入後即可作答並保存紀錄。
核心觀念
本題為民國 111 年公務人員高等考試三級考試(資訊處理類科)「程式設計」之經典非選擇題(佔 30 分),旨在評量考生對於模組化程式設計、同餘算術運算、位數處理及互動式輸入防呆控制之掌握程度。核心概念涵蓋:
- 位數分解與數值重組(Digit Extraction & Reconstruction):
- 透過整數除法(
/或//)與取模數運算(%),逐位分離五位數整數之萬分位(第 1 位)、千分位(第 2 位)、百分位(第 3 位)、十分位(第 4 位)與個位(第 5 位)。 - 運算完成後,再按十進位權重()相乘並加總,組裝回單一整數。
- 同餘代換的可逆性(Invertibility of Modular Arithmetic):
- 加密替換公式為:
- 由於在模數 10 下,加 5 的加法反元素(Additive Inverse)為 ,因此解密公式可直接寫為: 這說明在模數 10 的環境下,此位數代換運算本身具備自反性(Involutory Property)。
- 前導零格式化輸出(Formatting with Leading Zeros):
- 題目範例中,原數
12345加密後為09876。當密碼第一位數(萬分位)為 時,以整數形態傳回之數值為 。 - 在螢幕輸出時,必須指定五位數且不足補零的格式(例如 C 語言的
%05d或 Python 的{:05d}),方能符合題目要求的顯示外觀。
- 防呆驗證與旗標值迴圈控制(Sentinel-Controlled Loop & Validation):
- 程式採用旗標值(Sentinel Value)
0作為結束訊號。 - 非
0且不在 區間之整數視為無效輸入,須以巢狀迴圈進行重新輸入之提示與校驗。
解題方法
本題依題意分為三個部分循序推導與實作:
1. 加密函數 encrypt 演算法推導
設傳入之五位數整數為 (滿足 ):
- 位數萃取:
- 位數取代:
- 位置互換:
- 第 1 位與第 5 位互換:
- 第 2 位與第 4 位互換:
- 第 3 位不變:
- 數值重組: 傳回新整數:
2. 解密函數 decipher 演算法推導
設傳入之五位數密碼為 :
- 位數萃取: 若 (例如 ),,依然能精確萃取出 。
- 位置還原:
- 互換第 1 位與第 5 位:
- 互換第 2 位與第 4 位:
- 第 3 位不變:
- 位數代換還原:
- 數值重組: 傳回還原後之原整數:
3. 主程式流程控制
- 外層使用無窮迴圈
while (1)持續要求使用者輸入。 - 若輸入值為
0,印出感謝訊息後立即跳出迴圈並終止程式。 - 若輸入值小於
10000或大於99999,進入防呆驗證迴圈:印出「所輸入的數值並不是五位數之整數」及「請重新輸入:」,直到輸入合法五位數;若在重新輸入時使用者輸入0,亦能正確終止程式。 - 輸入有效後,依序呼叫
encrypt與decipher,並按題目格式要求輸出結果。
選項分析
本題為非選擇題(程式設計申論與實作題,總分 30 分)。依據國家考試評分要點,本題三大子題的評分標準與檢驗要點分析如下:
- 子題 1:加密函數
encrypt(配分約 10 分) - 核心檢驗:函式介面須為接收整數並回傳整數。能否正確處理陣列或位數的萃取、加 5 取模數,以及第 1/5 位、第 2/4 位的交換順序。
- 常見扣分點:將整數直接當作字串處理而未符合傳入/傳回「整數」之要求;或位數交換時覆蓋原本變數值而未保留暫存變數。
- 子題 2:解密函數
decipher(配分約 10 分) - 核心檢驗:能否精準執行逆運算。逆運算之順序必須與加密時相反(先換回位置,再進行數值反向代換;或先代換再換回位置,數學上兩者皆可,但位置對調不可遺漏)。
- 常見扣分點:當加密後首位為 0(整數僅剩四位數)時,若未考慮除以 10000 得到 0 的特徵,導致解密陣列長度錯置。
- 子題 3:主程式驗證與輸入防呆(配分約 10 分)
- 核心檢驗:迴圈控制架構是否能持續執行至使用者輸入
0為止;輸入值範圍檢驗()是否確實;終止訊息與重新輸入字串是否百分之百符合題目範例。 - 常見扣分點:漏寫邊界條件判斷、遇到非五位數直接結束程式而未給予重新輸入機會、未將首位為 0 之密碼以 5 位數補零格式(
%05d)印出。
解題技巧
- 同餘加減速算竅門:
在十進位系統中,若代換規則為 ,其還原為 。本題 ,,因此加密與解密的位數取代算式完全相同,皆為
(x + 5) % 10,解密時不必額外寫條件判斷式防止負數。 - 前導零的陷阱(The Leading Zero Trap):
密碼傳回值為
int,電腦在儲存整數時不會保留左側無意義的零( 會存為 )。顯示時務必使用%05d(C/C++)或f"{c:05d}"(Python),強制以五位數寬度並於左側補零輸出。 - 驗算檢查法:
第 四、 題30 分
抽象(abstract)類別、介面(interface)、覆寫(override)方法、多載(overload)方法是物件導向程式設計實現多型(polymorphism)的方式。下圖是人事系統中人員基本資料維護的類別圖,請說明類別圖中的覆寫方法及多載方法名稱(須說明理由),並使用 C++、Java 或 Python 程式語言撰寫 Person 類別與 Police 類別的程式碼。Police 類別之 GetPersonalInformation 函數必須傳回身分證(id_no)、姓名(name)、職稱(rank),UpdateRoleTitle 函數必須完整作更新 police_rank。(30 分)
🖼️【此處有附圖,請對照原卷】
類別圖內容如下:
-
Program
- Main(string[] args):void
-
Person <<abstract>>
-
id_no:string
-
name:string
-
- GetPersonalInformation():string
-
- UpdateRoleTitle():void
-
-
Police
-
- police_rank:PoliceRank
-
- Police(id:string, name:string)
-
- GetPersonalInformation():string
-
- UpdateRoleTitle(title:?):void
-
-
Staff
-
- staff_position:StaffPosition
-
- Staff(id:string, name:string)
-
- GetPersonalInformation():string
-
- UpdateRoleTitle(title:?):void
-
-
PoliceRank
-
- code:string
-
- rank:string
-
- PoliceRank(code:string, rank:string)
-
-
StaffPosition
-
- code:string
-
- title:string
-
- StaffPosition(code:string, title:string)
-
登入後即可作答並保存紀錄。
核心觀念
本題考查物件導向中的覆寫與多載:
- 覆寫(override):子類別重新定義父類別的方法,方法名稱、參數列與回傳型別須相符。透過父類別參考呼叫時,可依實際物件型別執行子類別版本。
- 多載(overload):同一類別或繼承關係中,方法名稱相同,但參數列不同。編譯器依呼叫時提供的參數決定要執行哪個版本。
- 抽象類別可以包含已實作的方法,也可以包含抽象方法;題圖標示
Person為抽象類別,並未將每個方法都標示為抽象方法。
解題方法
比對 Person 與子類別中的方法簽章:
Person.GetPersonalInformation與Police.GetPersonalInformation名稱、參數列及回傳型別均相同,因此Police.GetPersonalInformation是覆寫。Person.UpdateRoleTitle沒有參數,Police.UpdateRoleTitle(title: ?)有一個參數。兩者名稱相同、參數列不同,因此是多載,不是覆寫。- 圖中
title的型別未標明。由Police的欄位police_rank : PoliceRank可知,合理假設參數型別為PoliceRank;更新時將整個物件指定給police_rank,才能同時更新職稱代碼與職稱名稱。
程式碼(C++)
以下將 Person 保持為抽象類別,並依圖示保留無參數的 UpdateRoleTitle;有參數版本則負責完整更新警察職稱。題圖沒有交代無參數版本的實際更新規則,因此基底類別僅保留其方法介面。