C語言程式設計 考前總複習指南
關於「▶ 線上執行」按鈕:本指南「觀念白話說明」與「延伸範例」中,凡是完整、可獨立編譯的程式(含
main()),程式碼區塊右上角都會提供「▶ 線上執行」按鈕。點下後會在新分頁開啟
LiveCodes(開源線上程式編輯器,使用 Clang/WebAssembly 直接在瀏覽器內編譯執行 C/C++,程式碼完全不會送到任何伺服器),並自動帶入程式碼與執行結果主控台。
C語言程式設計 考前總複習指南
依「概念主題」整理 111~115 年 統測資訊科專業科目二(程式設計)考古題
使用說明
本指南涵蓋 module4/repo/cprog/ 內全部 78 題考古題(111~115 年,每年皆為「專業二」科目中程式設計相關題目),依「概念主題」而非「年度」重新編排,方便讀者按主題複習、抓出自己的弱點單元。
每個主題章節包含:
1. 觀念白話說明:用一小段可獨立編譯執行的示範程式碼,講解該主題的核心觀念,並額外附上 2 則延伸範例。 2. 考古題演練:該主題底下所有相關的歷屆考題,均附上原始考題掃描圖片(題幹、程式碼、選項忠實呈現原始排版)、正確答案,以及逐步追蹤(trace)的詳細解析。
版權提醒:本指南中的題目圖片均取自技專校院入學測驗中心歷屆統一入學測驗試題,僅供教學與自我練習使用,散布前請留意考古題本身的著作權歸屬。
目錄
| 章節 | 主題 | 題數 |
|---|---|---|
| 1 | 前處理器與編譯基礎 | 6 |
| 2 | 資料型態與型態轉換 | 7 |
| 3 | 運算子與優先序(含位元運算) | 10 |
| 4 | 選擇結構:if/switch/enum | 5 |
| 5 | 迴圈控制:for/while/do-while | 8 |
| 6 | 陣列與字串處理 | 12 |
| 7 | 指標 | 3 |
| 8 | 函式、變數範疇與遞迴 | 7 |
| 9 | 結構(struct)、typedef 與動態記憶體 | 7 |
| 10 | 排序演算法 | 2 |
| 11 | 數學函式庫與應用題 | 3 |
| 12 | 流程圖轉程式碼綜合應用 | 2 |
| 13 | 物件導向程式設計(C++ class) | 6 |
| 總計 | 78 |
第1章 前處理器與編譯基礎
C 程式在「編譯」之前,會先經過前處理器(Preprocessor)掃描所有以 # 開頭的指令(如 #include、#define),把它們替換/展開成實際的原始碼,之後才交給編譯器檢查語法並產生執行檔。前處理器指令有幾個重要規則:結尾不可加分號、#define 名稱 值 之間以空白分隔(不可用 =)、#define 定義出來的是「文字替換的巨集常數」而不是變數(不能被賦值)。
#include <stdio.h> // 前處理器指令:結尾不加分號
#define PI 3.14 // 巨集常數:名稱與值之間用空白,不用等號
int main(void) {
printf("%f\n", PI); // 編譯前,PI 會被文字替換成 3.14
return 0;
}
另外,區分「語法錯誤(Syntax error)」與「執行期錯誤(Run-time error)」也是常考觀念:語法錯誤在編譯階段就會被編譯器抓出來(例如少打分號、#define 對常數賦值);執行期錯誤則是程式語法完全合法、能編譯成功,但執行時才發生問題(例如除以 0、對未初始化的指標取值)。
延伸範例①:帶參數的巨集(function-like macro)與展開陷阱
#include <stdio.h>
#define SQUARE(x) x*x // 陷阱:沒有用括號包住x
#define SQUARE_SAFE(x) ((x)*(x)) // 正確寫法:整個參數都要加括號
int main(void) {
int a = 3;
printf("%d\n", SQUARE(a + 1)); // 展開成 a+1*a+1 = 3+1*3+1 = 7(不是預期的16!)
printf("%d\n", SQUARE_SAFE(a + 1)); // 展開成((a+1)*(a+1)) = 16,正確
return 0;
}
巨集只是「純文字替換」,不會像函式一樣先算好參數的值再代入,所以沒加括號保護就容易被外面的運算子影響。
延伸範例②:條件式編譯 #ifdef/#if
#include <stdio.h>
#define DEBUG 1
int main(void) {
#if DEBUG
printf("除錯模式:已開啟詳細訊息\n");
#endif
printf("程式正常執行結束\n");
return 0;
}
#if/#ifdef/#endif 也是前處理器指令,會依巨集是否定義/值為何,決定要不要把某段程式碼「編譯進去」,常用來切換除錯訊息或不同平台的程式碼。
111專業2Q36 — 答案:B
主題概念:C語言基本觀念/前處理器/編譯連結
解析:#include是前置處理指令(preprocessor directive),其結尾不需要(也不可以)加上分號,這與一般C語言敘述句不同。(A)(C)(D)皆為正確敘述,故錯誤的選項為(B)。
111專業2Q37 — 答案:D
主題概念:前處理器/巨集定義(#define)
解析:#define語法為「#define 名稱 值」,前面必須加上#字號、名稱與值之間以空白分隔(不可用=)、結尾不可加分號。(A)(B)缺少#;(C)有#但誤用=;只有(D) #define PI 3.14 完全符合正確語法。
112專業2Q37 — 答案:A
主題概念:編譯原理/除錯概念(語法錯誤 vs 執行期錯誤)
解析:語法錯誤會在「編譯」階段被編譯器檢查出來,故(A)正確。除以0(B)與指標未初始化就存取其內容(C)都是語法合法、但執行時才出錯的「執行期錯誤」。敘述結尾缺分號(D)則是編譯期就會抓到的語法錯誤,D把它歸類成執行期錯誤是錯的。
112專業2Q38 — 答案:C
主題概念:前處理器(#define巨集)/const常數/賦值運算
解析:問題出在行號3把Value3宣告為const int(常數),行號5卻又對它賦值,對常數賦值是編譯錯誤。只要刪除const,讓Value3變成一般int變數,賦值就合法,故選(C)。
112專業2Q39 — 答案:B
主題概念:前處理器巨集展開/運算式求值
解析:Value2巨集展開為(Value1 - 1) = (100 - 1) = 99,故Value3 = 99。CheckValue = Value1 + Value3 = 100 + 99 = 199,選(B)。
115專業2Q36 — 答案:C
主題概念:前處理器(巨集常數#define)/型態與編譯錯誤
解析:#define PI 3.14是巨集,編譯前會把程式中所有PI替換成字面常數3.14,PI並非變數(沒有記憶體位址),不能被賦值。行號6「PI = ...;」展開後等同於「3.14 = ...;」,對常數字面值賦值是不合法的左值(lvalue)錯誤,故在行號6出錯,選(C)。
第2章 資料型態與型態轉換
C 語言的基本資料型態(char、int、float、double……)各自佔用不同的記憶體空間、代表不同的數值範圍與精確度,選錯型態或忽略「整數運算」與「浮點數運算」的差異,是這個單元最常見的失分點。特別要注意:兩個整數相除是整數除法(無條件捨去小數),只要其中一個運算元是 float/double,才會變成浮點數除法。
int a = 7, b = 2;
float x = a / b; // 先做整數除法 7/2=3,再轉型 → x = 3.0(陷阱!)
float y = (float)a / b; // 先把a轉成float,7.0/2 → y = 3.5(正確拿到小數)
要知道某個型態或變數實際佔用多少 bytes,可用 sizeof() 運算子;在常見的 64 位元 x86 電腦上,int 是 4 bytes、char 是 1 byte、任何指標型態都是 8 bytes。
printf("%zu %zu %zu\n", sizeof(int), sizeof(char), sizeof(int *)); // 4 1 8
延伸範例①:整數溢位(overflow)
#include <stdio.h>
int main(void) {
short s = 32767; // short的最大值(2位元組有號整數)
s = s + 1; // 超過範圍,數值會「環繞」回最小值
printf("%d\n", s); // 印出 -32768
return 0;
}
每種整數型態都有固定的表示範圍,運算結果一旦超出範圍就會溢位、直接環繞到另一端,編譯器通常不會主動提醒。
延伸範例②:char是小型整數,可直接參與算術運算
#include <stdio.h>
int main(void) {
char c = 'A';
int code = c + 1; // char會先被自動提升為int再運算
printf("%c -> %d -> %c\n", c, code, code); // A -> 66 -> B
return 0;
}
C語言的char本質上就是一個位元組的整數(存放的是ASCII碼),可以直接加減,常被用來做「下一個字元」這類運算。
111專業2Q38 — 答案:A
主題概念:資料型態(float/double/int/char)
解析:float通常占4個bytes(單精度),double通常占8個bytes(雙精度),double的精確度高於float,兩者並不相同,故(A)敘述錯誤。
111專業2Q39 — 答案:B
主題概念:型態轉換/運算子(整數除法)
解析:x=a/b中a、b皆為int,是整數除法,7/2捨去小數得x=3。y=(float)a/b先把a強制轉型為float,7.0/2為浮點數除法,結果3.5,選(B)。
113專業2Q37 — 答案:C
主題概念:型態轉換/整數除法/浮點數精度
解析:問題根源在score[i]/10,兩者皆為int,屬整數除法,當score[i]<10時結果恆為0。正確作法是先把整數值原封不動累加到sum,最後才用sum/10(此時sum為double,做浮點除法)取得正確平均值,選(C)。
114專業2Q37 — 答案:D
主題概念:資料型態表示範圍
解析:32位元有號int使用1個bit表示正負號,可表示範圍為−2³¹~2³¹−1,最大值為2³¹−1;unsigned int全部位元皆用來表示大小,最大值為2³²−1,選(D)。
114專業2Q39 — 答案:C
主題概念:資料型態選擇/整數範圍
解析:char僅8位元(約−128~127),裝不下±20000;unsigned float不是合法的C資料型態;unsigned short int*是指標型態且無法表示負數,皆不合適。short在多數編譯器為16位元,範圍−32768~32767,恰可涵蓋−20000~20000,選(C)。
114專業2Q43 — 答案:C
主題概念:型態轉換/格式化輸出
解析:(short int)X=8表示X截斷後整數部分為8。(unsigned int)(X*4)以八進位輸出42(十進位34),故X∈[8.5, 8.75)。(int)(X*16)以十六進位輸出8a(十進位138),故X∈[8.625, 8.6875)。交集後X=8.625,驗證:8.625×4=34.5→截斷34(0o42✓);8.625×16=138.0(0x8a✓)。故printf("%6.4f",X)輸出8.6250,選(C)。
112專業2Q43 — 答案:C
主題概念:sizeof運算子/指標與陣列/資料型態大小
解析:s是char*指標,指向"ABC"。sizeof(s)是指標本身的大小,在64位元系統為8(存入CharPtrSize但未印出)。sizeof(*s)是指標所指向的char型態大小,為1,存入CharSize。*(s+2)即s[2],也就是'C'。輸出"1, C",選(C)。
第3章 運算子與優先序(含位元運算)
這是統測程式設計最常出陷阱題的單元。核心觀念是運算子優先序(Operator Precedence):不同運算子在同一運算式中執行的先後順序不同,寫法「看起來像什麼」不代表「真的照那個順序算」。常見易混淆的優先序組合:
- 關係運算子 ==、!= 的優先序高於位元運算子 &、^、|(例如 a^b==x 其實是 a^(b==x),不是 (a^b)==x)。 - 位移運算子 <<、>> 的優先序低於加減乘除,但高於位元邏輯運算子與比較運算子。 - 型態轉型 (int) 的優先序高於乘除,(int)a*b 是 ((int)a)*b,不是 (int)(a*b)。 - 前置遞增 ++a(先加後用)與後置遞增 a++(先用後加)的差異。 - 位元運算子 &(AND)、|(OR)、^(XOR)是逐位元操作,與邏輯運算子 &&、||(只看整體真假)意義完全不同。
int a = 9, b = 7;
printf("%d\n", a ^ b); // 位元XOR:1001^0111=1110=14
unsigned char x = 0x0a, y = 0x05;
if (x & y) printf("x&y=%d\n", x & y); // 位元AND:0(不成立→跳過)
else printf("x&&y=%d\n", x && y); // 邏輯AND:x非0且y非0→真(1)
延伸範例①:用位移與位元遮罩(mask)取出指定的一個位元
#include <stdio.h>
int main(void) {
int flags = 0x0A; // 十六進位0x0A = 二進位1010
int bit1 = (flags >> 1) & 1; // 先右移1位,再用&1取出最低位
printf("bit1 = %d\n", bit1); // 1
printf("flags*4 = %d\n", flags << 2); // 左移2位,相當於乘以4
return 0;
}
>>/<<常被用來做「乘除2的次方」,也常搭配&當作遮罩,只留下某一個位元的值,是位元運算最實用的組合技。
延伸範例②:前置/後置遞增在陣列索引中的差異
#include <stdio.h>
int main(void) {
int arr[5] = {10, 20, 30, 40, 50};
int i = 0;
printf("%d\n", arr[i++]); // 先用i=0取值(10),之後i才變成1
printf("%d\n", arr[++i]); // i先變成2,才取arr[2](30)
return 0;
}
把遞增運算子放進陣列索引裡是常見的優先序陷阱:i++用的是舊值,++i用的是新值,兩者取到的元素會不一樣。
111專業2Q40 — 答案:C
主題概念:位元運算(XOR)
解析:^為位元XOR運算子。a=9的二進位為1001,b=7的二進位為0111,逐位元XOR:1001^0111=1110=十進位14,選(C)。
111專業2Q41 — 答案:D
主題概念:運算子優先序/邏輯運算
解析:逐一驗證:(A) false||false=false;(B) true&&false=false;(C) false||false=false;(D) 1<3為true,!(1>=3)為true,true&&true=true。只有(D)為真,選(D)。
112專業2Q44 — 答案:D
主題概念:位元運算(位移運算子<<、>>)/運算子優先序
解析:減法優先於位移,乘法優先於位移。Num1 << Num3-1 = 10 << (3-1) = 10 << 2 = 40。Num2 * Num1 >> 1 = (5×40) >> 1 = 200 >> 1 = 100,選(D)。
112專業2Q45 — 答案:A
主題概念:位元運算子(&)與邏輯運算子(&&)的差異
解析:a=10(1010),b=5(0101)。a & b位元AND=0000=0,if(0)為假,執行else。a && b邏輯AND:a非0且b非0,結果為1,輸出"a&&b=1",選(A)。
113專業2Q41 — 答案:D
主題概念:運算子優先序/位元運算
解析:%優先序高於+,故(D)先算2%5=2;(A+124)&2是對第1個位元(值2)做遮罩,只會得到0或2;最後加2,結果恆為2或4,皆為偶數,且與A值無關,選(D)。(A)(B)(C)的奇偶性都會隨A值改變。
114專業2Q40 — 答案:C
主題概念:運算子優先序/位元運算
解析:C語言中==的優先序高於^,^的優先序又高於&&。因此a^b==x實際解析為a^(b==x),不是(a^b)==x!對照選項,(C) x=(x*x%2&&a^(b==x))?1:0;與原if-else完全等價,選(C)。(A)(D)誤把a^b==x當成(a^b)==x,落入優先序陷阱。
114專業2Q45 — 答案:D
主題概念:運算子優先序/前置與後置遞增遞減
解析:*、/、遞增遞減的優先序皆高於+。a++為後置遞增,運算式中使用a的舊值1(之後才變2);++d為前置遞增,d先變8才參與運算。x = a++/b + c*++d = (1/3) + (5×8) = 0(整數除法)+ 40 = 40,選(D)。
114專業2Q48 — 答案:B
主題概念:位元運算/運算子優先序
解析:位元運算優先序:~(一元)> << > ^ > |。故~a<<2^b|a解析為(((~a)<<2)^b)|a。a=128=0x80,~a=0xFFFFFF7F,左移2位得0xFFFFFDFC;與b=0xFF做XOR,最低byte 0xFC^0xFF=0x03,得0xFFFFFD03;再與a=0x80做OR,最低byte 0x03|0x80=0x83;最後&0xff取最低byte得0x83。以std::hex輸出"83",選(B)。
115專業2Q39 — 答案:A
主題概念:位元運算(AND/OR/XOR)/條件運算子(三元運算子)
解析:b=127(01111111),c=128(10000000)。b|c=11111111=255,(b|c)==0為假,三元運算子選擇b&c。b&c=01111111 & 10000000 = 00000000 = 0,選(A)。
115專業2Q44 — 答案:B
主題概念:型態轉換/運算子優先序(強制轉型與乘法)
解析:total = g_n*30 + r_n*20 = 3×30+2×20 = 130。s_flag='g'不等於's',該行不執行(順帶一提:(int)優先序高於乘法,即使執行也是((int)total)*1.1,屬常見優先序陷阱);buy='n'不等於'y',也不執行。total維持130,選(B)。
第4章 選擇結構:if/switch/enum
選擇結構依條件決定要執行哪一段程式碼。switch-case 特別要注意:沒有寫 break 就會「貫穿(fall-through)」到下一個 case,繼續往下執行直到遇到 break 或結束。enum(列舉)則是把一組具名常數依序編號(預設從0開始,也可指定初值後續遞增)。
enum State { Run = 1, Stop, Pause = 3, Exit }; // Run=1, Stop=2, Pause=3, Exit=4
int i = 2;
switch (i) {
case 1: printf("A"); break;
case 2: printf("B"); // 沒有break!
case 3: printf("C"); break; // 會被貫穿執行
default: printf("D");
}
// 輸出:"BC"
延伸範例①:if-else if 鏈與邊界值
#include <stdio.h>
int main(void) {
int score = 60;
if (score >= 90) printf("A\n");
else if (score >= 80) printf("B\n");
else if (score >= 60) printf("C\n"); // 60剛好落在這一段
else printf("F\n");
return 0;
}
if-else if鏈是「由上往下,第一個成立的條件就執行」,寫等第、分級這類題目時,邊界值(例如剛好60分)要特別注意用>=還是>。
延伸範例②:switch搭配break的標準寫法
#include <stdio.h>
int main(void) {
int day = 3;
switch (day) {
case 1: case 2: case 3: case 4: case 5:
printf("平日\n");
break;
case 6: case 7:
printf("假日\n");
break;
default:
printf("不合法的輸入\n");
}
return 0;
}
多個case可以疊在一起共用同一段程式碼(不寫break,直接讓它們貫穿到共同的處理邏輯),這是switch很常見的簡潔寫法。
111專業2Q42 — 答案:D
主題概念:選擇結構(if-elseif-else)
解析:a=45,a>=60為假,a>=50亦為假,執行else分支:y=a+3=48,選(D)。
113專業2Q39 — 答案:A
主題概念:switch-case/位元運算/字元運算
解析:i=3(00000011)。i&0x0e(00001110)=00000010=2,2%5=2,進入case(2)。case(2)輸出'0'+9='9';無break,貫穿至case(3)輸出'a'+9='j';再貫穿至default輸出'z'。總輸出"9jz",選(A)。
113專業2Q40 — 答案:A
主題概念:enum列舉/流程控制(if條件判斷)
解析:Run=1,Stop=2,Pause=3,Exit=4。Command=1:第一個if成立→Stop(2);第三個if(StateMachine==Stop成立)→Exit(4)。結果為Exit,選(A)。
114專業2Q38 — 答案:C
主題概念:關係運算子/邏輯除錯
解析:x+y=35(≥32),flag=(x+y<32)=0,!flag為真,反而印出「Go!」,邏輯顛倒。將行6改為x+y>=32,flag直接代表「總和≥32」,!flag才正確對應「總和<32」該印的「Go!」,選(C)。
115專業2Q42 — 答案:B
主題概念:switch-case選擇結構/字串處理
解析:字串中A出現4次、B出現3次、C出現2次、數字1/2/3各1次(共3次)、符號3個。switch中沒有case 'C':分支,因此字元'C'會落入default,被計入c_sym而非c_C(c_C恆為0)。c_A=4, c_B=3, c_C=0, c_digi=3, c_sym=3(符號)+2(C字元)=5。輸出"4,3,0,3,5",選(B)。
第5章 迴圈控制:for/while/do-while
三種迴圈的差異:for 適合已知次數的迴圈;while 先判斷條件再執行;do-while 先執行一次本體,再判斷條件(所以本體至少會跑一次)。搭配 break(跳出整個迴圈)與 continue(跳過本次剩餘程式碼、直接回到條件判斷)會讓追蹤變得複雜,務必逐行手動trace。另外一個常考陷阱:若迴圈計數器宣告為 unsigned,遞減到0以下時會整數溢位環繞成一個超大正數,導致條件恆成立、形成無窮迴圈。
unsigned short i;
for (i = 5; i >= 0; i--) { // 危險!i是unsigned,永遠 >= 0
printf("%d ", i);
} // i從0再減1不會變成-1,而是環繞成65535 → 無窮迴圈
延伸範例①:break跳出整個迴圈、continue只跳過本次
#include <stdio.h>
int main(void) {
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) continue; // 偶數:跳過本次,不印
if (i > 7) break; // 大於7:整個迴圈直接結束
printf("%d ", i);
}
printf("\n"); // 輸出: 1 3 5 7
return 0;
}
continue只跳過「這一輪」剩下的程式碼、直接回到迴圈條件;break則是把整個迴圈當場中止,兩者效果差很多,追蹤程式時務必分清楚。
延伸範例②:巢狀迴圈——外層跑幾次、內層就整組跑幾次
#include <stdio.h>
int main(void) {
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++)
printf("%d*%d=%d ", i, j, i * j);
printf("\n");
}
return 0;
}
外層迴圈每跑一輪,內層迴圈就會完整地從頭跑到尾一次;這題外層3次、內層3次,總共執行3×3=9次乘法運算。
111專業2Q43 — 答案:C
主題概念:迴圈控制(for迴圈)
解析:i依序為1、3、5、7(每次遞增2)。total累加:0+1+3+5+7=16。當i=9,9<8不成立,迴圈結束,選(C)。
111專業2Q44 — 答案:D
主題概念:迴圈控制(while)/算術運算(輾轉相除法)
解析:此為輾轉相除法(求最大公因數,GCD)。初始r=30%42=30。第1輪:a=42,b=30,r=42%30=12。第2輪:a=30,b=12,r=30%12=6。第3輪:a=12,b=6,r=12%6=0,迴圈結束。y=b=6,選(D)。
112專業2Q46 — 答案:B
主題概念:迴圈控制(do-while)/前置與後置遞增運算子(++)/變數遮蔽(shadowing)
解析:main()內的區域變數x遮蔽了全域x,選項A、D修改全域x完全無效。原本用前置遞增++x,第1輪x先變1才比較1<=0為假,只跑1次。改為後置遞增x++(選項B)後,比較時用尚未遞增的值:第1輪0<=0真→x變1;第2輪y=10sin(1)=8.415,1<=8.415真→x變2;第3輪y=10sin(2)≈9.09,2<=9.09真→x變3;第4輪y=10sin(3)≈1.41,3<=1.41假,結束,共執行4次,選(B)。
114專業2Q41 — 答案:D
主題概念:迴圈控制/布林代數(真值表推導)
解析:X對應A、Y對應B、Z對應F。逐一追蹤四組(X,Y):(0,0)→Z=0;(0,1)→Z=0;(1,0)→Z=1;(1,1)→Z=1。可見F恆等於A。代入(D):(A+B̄)(A+B)展開=A+AB+AB̄+BB̄=A(1+B+B̄)=A,與四組真值皆相符,選(D)。
115專業2Q35 — 答案:C
主題概念:陣列(二維字元陣列)/迴圈控制
解析:正確急救步驟為「沖、脫、泡、蓋、送」。陣列S中索引0,2,4,6,8依序為"沖","脫","泡","蓋","送",正是正確順序;索引1,3,5,7為干擾用假資料。故迴圈需從i=0開始、每次加2、跑到i=8,即for(i=0; i<9; i+=2),選(C)。
115專業2Q40 — 答案:C
主題概念:字串處理/迴圈控制(continue敘述)
解析:逐字元比對:i=1('b'='b')c=1;i=3('#'='#')c=2;i=8('5'='5')c=3,此時觸發continue,跳過i++直接回到while判斷,i仍為8,再次比對stringA[8]與stringB[8]仍相等,c再加1變4(此時c==3已不成立),i++變9;之後i=10('*'='*')c=5。最終c=5,選(C)。
115專業2Q41 — 答案:D
主題概念:型態轉換(unsigned溢位/迴圈無窮迴圈)
解析:i宣告為unsigned short,其值永遠不會小於0,故i>=0恆為真,形成無窮迴圈(i從0再i--會溢位環繞成65535,繼續以超出陣列範圍的索引存取),導致程式跑不到後面的printf。將unsigned刪除,讓i變成有號short,遞減到-1時i>=0為假,迴圈才能正常結束,選(D)。
115專業2Q43 — 答案:C
主題概念:巢狀迴圈/迴圈控制追蹤
解析:i=0:sum+=0→0;j=0~5累加0+1+2+3+4+5=15→sum=15;i變2。i=2:sum+=2→17;j=2~5累加14→sum=31;i變4。i=4:sum+=4→35;j=4~5累加9→sum=44;i變6,6<5為假,跳出。sum=44,選(C)。
第6章 陣列與字串處理
C 語言的字串本質上就是一個「以 \0(null字元)結尾」的 char 陣列,%s 會從指定位置開始輸出,直到遇到 \0 為止。陣列若宣告的元素個數大於提供的初始值個數,未明確給值的元素會自動補 0。二維字元陣列(如 char S[10][4])常被用來存放一組固定長度的中文字串(每個中文字在UTF-8編碼下通常占3 bytes,加上結尾的\0共4 bytes)。
int arr[4] = {1, 2, 3}; // 元素個數宣告4個,只給3個值
printf("%d", arr[3]); // 未給值的 arr[3] 自動補0 → 印出 0
char name[6] = "Alex"; // 字元陣列:'A','l','e','x','\0',(未用)
printf("%s", name); // 印到 '\0' 為止 → 印出 Alex
延伸範例①:strlen()/strcmp()——字串內容不能直接用 == 比較
#include <stdio.h>
#include <string.h>
int main(void) {
char a[] = "cat";
char b[] = "cat";
printf("strlen(a) = %zu\n", strlen(a)); // 3(不含結尾的'\0')
printf("strcmp(a,b) = %d\n", strcmp(a, b)); // 0代表兩字串內容相同
// if (a == b) 是常見錯誤!這樣比較的是兩個陣列的記憶體位址,不是內容
return 0;
}
字串比較內容要用strcmp()(回傳0代表相同),比較長度用strlen();用==比較的是陣列(指標)位址,幾乎不會是你要的結果。
延伸範例②:二維陣列(矩陣)的宣告與走訪
#include <stdio.h>
int main(void) {
int matrix[2][3] = { {1, 2, 3}, {4, 5, 6} };
for (int r = 0; r < 2; r++) {
for (int c = 0; c < 3; c++)
printf("%d ", matrix[r][c]);
printf("\n");
}
return 0;
}
二維陣列可以想成「陣列的陣列」,第一個索引是列(row)、第二個是行(column),用巢狀迴圈走訪時,索引順序要對應到宣告時的順序。
111專業2Q45 — 答案:C
主題概念:陣列/迴圈控制
解析:Array宣告4個元素但只給3個初始值,Array[3]自動補0。迴圈i從1到3:Sum+=Array[1](2)→2;+=Array[2](3)→5;+=Array[3](0)→5。最終Sum=5,選(C)。
111專業2Q46 — 答案:D
主題概念:指標/字串處理
解析:ptr原指向Str[0]('a')。前置遞增++ptr先讓ptr移向Str[1]('b'),才傳給printf。%s從該位址輸出直到\0,故輸出"bcde",選(D)。
112專業2Q47 — 答案:D
主題概念:陣列/字串處理/迴圈與索引運算
解析:迴圈跑11次,每次取Dictionary[Search]存入ShowMessage[Index],再Search += Count。若Count=3,Search依序為0,3,6,9,...,30,對照Dictionary逐字元編號,取出結果恰為"ILoveTaiwan",選(D)。
113專業2Q35 — 答案:C
主題概念:陣列/字串處理
解析:StepName索引依序為:0=拖,1=脫,2=送,3=沖,4=蓋,5=泡,6=送。正確急救步驟為「沖、脫、泡、蓋、送」,對應索引3、1、5、4、6,選(C)。
113專業2Q38 — 答案:B
主題概念:指標/陣列
解析:Data只給8個初值,Data[8]、Data[9]預設為0。pData指向&Data[2]。(B) *(pData+3) = Data[5] = 12,恰等於Data[5]本身,判斷式恆真,選(B)。(A)(C)(D)代入計算後皆與選項敘述矛盾。
114專業2Q35 — 答案:B
主題概念:字串陣列/函式呼叫(指標參數)
解析:原始陣列(索引0~4)=脫,蓋,送,沖,泡。以(B)模擬:swap(0,3)→[沖,蓋,送,脫,泡];swap(3,1)→[沖,脫,送,蓋,泡];swap(2,4)→[沖,脫,泡,蓋,送]。結果恰為「沖脫泡蓋送」,選(B)。
114專業2Q36 — 答案:D
主題概念:指標與字串/區域變數遮蔽全域變數
解析:main()內的區域陣列char ptr[20]遮蔽了全域指標char *ptr,故第7、8行的ptr皆指向local陣列"Main"。*ptr = *str只是把str第一個字元'T'指定給ptr[0],把原本的'M'換成'T',其餘不變,ptr變成"Tain",選(D)。
114專業2Q42 — 答案:B
主題概念:字串比對/迴圈與陣列追蹤
解析:sizeof(query)固定為3('i','s','\0')。字串中出現"is"(後接's')的位置有3處,match_count累加到3;外層迴圈每輪結尾都執行ans=0,故最終ans恆為0。輸出"3, 0, 3",選(B)。
114專業2Q44 — 答案:B
主題概念:陣列與指標
解析:n[1]=100後陣列變為{1,100,3,4,5};*n即n[0],設定為1(值不變)。以*(n+i)(等同n[i])依序輸出"1","100","3","4","5",串接為"1100345",選(B)。
115專業2Q37 — 答案:D
主題概念:陣列/位元運算/迴圈追蹤
解析:迴圈從i=7遞減到i=0後再執行i--使i變-1,條件i>=0不成立才結束,故執行到行號13時i=-1(A、B皆錯)。逐位元把x[]、y[]依序轉成十進位:X=254(C錯),Y=127(D對)。
115專業2Q38 — 答案:C
主題概念:位元運算/格式化輸出(printf精度控制)
解析:由上題分析,x與y在索引7與索引0處不同,其餘6個位置相同,故count=2。(float)count/8 = 2/8 = 0.25。%.3f表示小數點後固定輸出3位,輸出"0.250",選(C)。
115專業2Q45 — 答案:B
主題概念:陣列/字元運算(ASCII加法)
解析:迴圈i=1,2,3對used[i]、name[i]各加1。name[3]原為'x',加1後成為ASCII碼加1的字元'y',選(B)。used[1]=3(非2),used[3]=5(非4),name[1]='m'(非'B')。
第7章 指標
指標變數存放的是「記憶體位址」。&x 取得變數x的位址;*p 則是「解參考(dereference)」,取得指標p所指向位址中存放的值。函式參數若用一般型態(如 int)傳遞,函式內對參數的修改不會影響呼叫端的原始變數(傳值呼叫,call by value);若要讓函式能修改呼叫端變數,必須改用指標型態當參數(傳址呼叫,call by reference)。
void inc_by_value(int x) { x = x + 1; } // 只改到函式內的複本,main的變數不受影響
void inc_by_reference(int *x) { *x = *x + 1; } // 透過位址找到原始變數並修改它
int main(void) {
int a = 10;
inc_by_value(a); printf("%d\n", a); // 10(沒變)
inc_by_reference(&a); printf("%d\n", a); // 11(真的改到了)
}
延伸範例①:指標算術(pointer arithmetic)與陣列名稱的關係
#include <stdio.h>
int main(void) {
int arr[3] = {100, 200, 300};
int *p = arr; // 陣列名稱會自動退化成指向第一個元素的指標
printf("%d\n", *p); // 100
printf("%d\n", *(p + 1)); // 200,指標+1會依型態大小跳過一個int
printf("%d\n", p[2]); // 300,p[i]其實就是*(p+i)的另一種寫法
return 0;
}
陣列名稱在大部分情況下會自動退化成「指向第一個元素的指標」,p+1不是位址加1個byte,而是加1個int的大小(通常是4 bytes)。
延伸範例②:指標的指標(double pointer)
#include <stdio.h>
int main(void) {
int x = 5;
int *p = &x;
int **pp = &p; // pp存放的是「p這個指標變數」的位址
**pp = 99; // 透過雙重解參考,一路改到最原始的x
printf("%d\n", x); // 99
return 0;
}
**pp要先解一次參考拿到p,再解一次參考拿到x,雙重指標常用在函式需要「修改一個指標本身所指向的位置」的情境。
112專業2Q41 — 答案:A
主題概念:指標/傳址呼叫(call by reference)
解析:要讓main()中的x能被inc()內的運算真正更動,必須改成傳址呼叫:傳入x的位址&x,inc()的參數改成指標型態接收,並在函式內用解參考*xin存取/修改實際值,選(A)。
112專業2Q48 — 答案:D
主題概念:指標/型態相容性/位址運算子(&)
解析:p只能合法(無警告)指向int型態的位址。(A)(B)MyName是char陣列,型態為char*,與int*不符;(C)MyWeight本身是double值(非位址),型態錯誤更明顯;(D)&MyAge是int的位址,型態為int*,與p完全相符,選(D)。
115專業2Q46 — 答案:A
主題概念:指標
解析:p1指向i2,p2指向i1。先執行*p1 = i1+1 = 2,即把i2設為2(未變化)。接著*p2 = i2+2,此時i2已是2,故*p2 = 4,即把i1設為4。最終i1=4,選(A)。
第8章 函式、變數範疇與遞迴
變數範疇(scope)決定一個變數能在程式的哪個範圍被看見/使用。寫在所有函式之外的是全域變數(global variable),整個程式檔都能存取;寫在函式內部的是區域變數(local variable),只在該函式(或該區塊)內有效。若區域變數與全域變數同名,區域變數會遮蔽(shadow)全域變數,這段範圍內對這個名字的存取都指向區域變數。
遞迴(recursion)是函式呼叫自己,一定要有能讓遞迴停止的「終止條件(base case)」,否則會無窮遞迴。
int count = 0; // 全域變數
void demo(int n) {
int count = 100; // 區域變數,遮蔽了全域的count
printf("%d\n", count); // 印的是100,不是全域的0
}
int fact(int n) { // 遞迴範例:階乘
if (n <= 1) return 1; // 終止條件
return n * fact(n - 1); // 呼叫自己,範圍縮小
}
延伸範例①:static區域變數——在函式呼叫之間保留上次的值
#include <stdio.h>
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;
}
一般的區域變數每次呼叫函式都會重新產生、重新初始化;加上static後,變數只會被初始化一次,之後每次呼叫都延續上一次結束時的值。
延伸範例②:費氏數列——有兩個終止條件的遞迴
#include <stdio.h>
int fib(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
return fib(n - 1) + fib(n - 2);
}
int main(void) {
for (int i = 0; i < 8; i++)
printf("%d ", fib(i)); // 0 1 1 2 3 5 8 13
printf("\n");
return 0;
}
遞迴不一定只有一個終止條件;費氏數列需要同時檢查n==0與n==1兩種情況,才能讓遞迴正確地停下來。
111專業2Q47 — 答案:C
主題概念:函式呼叫/型態轉換(浮點數運算)
解析:呼叫Func(5,3),A=w*h/2.0:先計算w*h=15(整數乘法),再除以2.0(浮點數),因除數為浮點數整個除法以浮點方式運算,得15/2.0=7.5,選(C)。
111專業2Q48 — 答案:D
主題概念:函式原型宣告/回傳型態
解析:需要「接收兩個整數」排除(A);需要「回傳帶小數精確度的值」排除(B)(無回傳值)與(C)(回傳int無小數)。只有(D)同時符合兩項條件,選(D)。
112專業2Q36 — 答案:B
主題概念:前處理器/函式原型宣告(prototype)/命令列參數(argc, argv)
解析:main()呼叫sub()時,sub()的定義在main()之後,且沒有事先的函式原型宣告,編譯器不知道sub()的簽名而編譯失敗。行號3已經寫好原型宣告卻被註解掉,去掉//讓它生效即可解決「呼叫先於宣告」的問題,選(B)。
112專業2Q40 — 答案:D
主題概念:全域變數與區域變數/函式呼叫傳值(call by value)/變數遮蔽(shadowing)
解析:全域sum=1,x=10;main()區域sum=3。呼叫inc(x),x以傳值方式傳入,xin接收複本10。inc()內區域sum=2;sum=sum+xin=12;xin++只影響函式內複本,不影響main的x。回傳12給main的sum。x本身未被修改仍是10。輸出"12, 10",選(D)。
112專業2Q42 — 答案:A
主題概念:全域變數/區域變數/變數範疇(scope)
解析:行號2寫在所有函式之外,是全域變數;行號4寫在inc()內部,是區域變數;行號10寫在main()內部,也是區域變數。答案為(A)。
113專業2Q36 — 答案:C
主題概念:變數作用域(全域/區域變數)/函式
解析:a,b,c是main()內的區域變數,而函式f()定義在main()之外、位置更早,f()內直接使用a,b,c,這些名稱在f()的作用域中並不存在,編譯器找不到而報錯。解法是把a,b,c的宣告移到最前面成為全域變數,使f()與main()都能存取,選(C)。
115專業2Q49 — 答案:A
主題概念:遞迴/全域變數
解析:foo(3):3≠1,count變1,遞迴foo(2)。foo(2):2≠1,count變2,遞迴foo(1)。foo(1):1==1,終止條件成立,直接返回。回main,count此時為2,++count前置遞增,先加1變3,再輸出3,選(A)。
第9章 結構(struct)、typedef 與動態記憶體
struct 讓我們把多個不同型態的資料包成一個自訂的複合型態;typedef 可以幫複雜的型態取一個簡短的別名。存取結構成員:若手上是「結構變數」用 .(例如 student.score);若手上是「指向結構的指標」則用 ->(例如 p->score,等同 (*p).score)。要留意 sizeof(struct) 常常因為記憶體對齊(alignment)而大於各成員大小的總和。動態配置記憶體時,malloc() 只會配置你要求的大小——如果結構裡有指標成員(如 char *name),malloc(sizeof(CLASS)) 並不會連帶替這個指標配置到它指向的字串空間,還需要額外呼叫一次 malloc() 才能安全使用它。
typedef struct {
int id;
float score;
} Student;
Student s1; // 結構變數:用 .
s1.id = 1; s1.score = 88.5;
Student *p = &s1; // 結構指標:用 ->
p->score = 90.0; // 等同於 (*p).score = 90.0;
延伸範例①:結構陣列——儲存多筆記錄
#include <stdio.h>
typedef struct { char name[10]; int score; } Student;
int main(void) {
Student list[2] = { {"Amy", 88}, {"Ben", 92} };
for (int i = 0; i < 2; i++)
printf("%s: %d\n", list[i].name, list[i].score);
return 0;
}
結構陣列讓我們用同一個型態一次管理多筆資料(例如全班的學生資料),每個元素都是一份完整的結構,用.存取各自的成員。
延伸範例②:malloc()動態配置一個結構、用完記得free()
#include <stdio.h>
#include <stdlib.h>
typedef struct { int id; float score; } Record;
int main(void) {
Record *r = (Record *)malloc(sizeof(Record));
r->id = 1;
r->score = 95.5;
printf("id=%d score=%.1f\n", r->id, r->score);
free(r); // 用malloc配置的記憶體,使用完務必呼叫free()釋放,避免記憶體洩漏
return 0;
}
malloc()會在執行期依需要的大小向系統要一塊記憶體、回傳指向它的指標;這塊記憶體不會自動消失,程式用完後要自己呼叫free()歸還。
111專業2Q50 — 答案:C
主題概念:結構(structures)
解析:struct成員變數的資料型態沒有「必須互不相同」的限制,一個結構完全可以包含多個相同型態的成員,故(C)敘述錯誤。
113專業2Q43 — 答案:A
主題概念:typedef/結構(struct)/指標與陣列運算
解析:學號28的學生資料存於student[28-1]=student[27]。student為結構陣列,取用其score成員應以.運算子存取,即student[27].score,選(A)。
113專業2Q44 — 答案:B
主題概念:排序演算法(泡沫排序)/迴圈次數計算
解析:標準泡沫排序對n筆資料排序,兩兩比較總次數為n(n-1)/2。n=N=50,故比較次數為50×49/2=1225,選(B)。
114專業2Q46 — 答案:A
主題概念:結構與指標/動態記憶體配置
解析:malloc(sizeof(CLASS))只配置了CLASS結構本身(含name這個指標欄位所佔的8 bytes空間),並沒有替name指標另外配置字串緩衝區。(*class).name此時是未初始化的野指標,strcpy把"Brown"寫入這個無效記憶體會導致未定義行為,選(A)。
114專業2Q49 — 答案:B
主題概念:sizeof運算子/資料型態大小
解析:m=sizeof(int)=4;n=sizeof(char)=1;p=sizeof(int *)=8(64位元指標為8 bytes)。逐一檢驗:m≧n即4≧1成立,選(B)。
114專業2Q50 — 答案:A
主題概念:sizeof運算子/結構與記憶體對齊
解析:CLASS結構含int id(4 bytes)+char *name(8 bytes指標,因對齊前面補4 bytes padding)+float score(4 bytes),sizeof(CLASS)=g=24 bytes。a=sizeof(classA)=50×24=1200(classA是main()內的CLASS陣列);b=sizeof(classB[59])只是單一元素大小=24(與g相同);d=sizeof(i)=4。四者中a=1200遠大於其他,選(A)。
115專業2Q50 — 答案:C
主題概念:結構(struct)/陣列
解析:(A)(B)的name只是一個字元陣列,只能存放「一個」字串,無法同時儲存100隻毛小孩各自的名字。(D)語法不合法,C語言不能在定義struct標籤時直接於標籤後接陣列大小。(C)正確定義結構pet,再宣告Pets[100]為此結構的陣列,可獨立儲存100隻毛小孩各自完整的四項資料,選(C)。
第10章 排序演算法
泡沫排序(Bubble Sort)是最基礎的排序法:每一輪都掃過整個(或剩餘)陣列,只要相鄰(或指定的)兩個元素順序不對就交換,反覆進行直到整個陣列排序完成。追蹤這類題目時,務必逐行手寫每一輪比較與交換後的陣列狀態,不要用直覺猜答案。另外要留意:交換兩個數值的函式若用一般變數(傳值)當參數,函式內的交換只會影響函式內的複本,不會真的改到呼叫端的陣列,必須用指標才能真正交換。
void swap_wrong(int a, int b) { int t=a; a=b; b=t; } // 傳值:呼叫端不會被真的交換
void swap_right(int *a, int *b) { int t=*a; *a=*b; *b=t; } // 傳指標:才能真的交換
延伸範例①:完整的泡沫排序(bubble sort)函式
#include <stdio.h>
void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; }
int main(void) {
int arr[5] = {5, 2, 4, 1, 3};
for (int i = 0; i < 5 - 1; i++)
for (int j = 0; j < 5 - 1 - i; j++)
if (arr[j] > arr[j + 1])
swap(&arr[j], &arr[j + 1]);
for (int i = 0; i < 5; i++) printf("%d ", arr[i]); // 1 2 3 4 5
printf("\n");
return 0;
}
每一輪都把相鄰元素兩兩比較,較大的往後推;跑完n-1輪之後,最大的幾個元素就會像泡泡一樣依序「浮」到陣列尾端。
延伸範例②:選擇排序(selection sort)——對照另一種排序策略
#include <stdio.h>
int main(void) {
int arr[5] = {5, 2, 4, 1, 3};
for (int i = 0; i < 5; i++) {
int min = i;
for (int j = i + 1; j < 5; j++)
if (arr[j] < arr[min]) min = j;
int t = arr[i]; arr[i] = arr[min]; arr[min] = t;
}
for (int i = 0; i < 5; i++) printf("%d ", arr[i]); // 1 2 3 4 5
printf("\n");
return 0;
}
選擇排序每一輪都先「選出」剩餘範圍中最小的元素,再跟目前位置交換一次;跟泡沫排序不同,選擇排序每一輪只交換一次。
113專業2Q47 — 答案:C
主題概念:陣列/排序演算法追蹤
解析:陣列初始值('a'=97)為[1,3,5,7,9,2,4,6,8,0,97]。這個雙層迴圈的寫法是:對每個min,內層i掃描整個陣列,只要numbers[i]<numbers[min]就立刻交換,逐步把最小值往前擠、其餘值往後擠成遞減排列。實際逐輪模擬,最終陣列變為[97,9,8,7,6,5,4,3,2,1,0],選(C)。
113專業2Q48 — 答案:A
主題概念:函式呼叫/參數傳遞(指標與整數型態不符)
解析:swap()函式參數宣告為void swap(int a, int b),要求傳入int(傳值);但呼叫時傳入numbers+i、numbers+min是指標運算的結果,型態為int*,與函式期望的int不符,故編譯器報錯,選(A)。
第11章 數學函式庫與應用題
<math.h> 提供 sqrt()(開根號)、pow(x,y)(x的y次方)、sin()(正弦)等常用數學函式;使用這些函式時要留意定義域(domain)問題,例如對負數開根號在數學上無定義,執行時會產生 domain error。另外,判斷一個連續函數在區間 [m, n] 內是否存在零點,數學上常用勘根定理(Intermediate Value Theorem):只要區間兩端點的函數值 f(m) 與 f(n) 異號(相乘小於等於0),該區間內就必定存在一個解。
#include <math.h>
double f(double x) { return x - 2; } // 假設的連續函數
if (f(1) * f(3) <= 0) {
printf("[1,3] 區間內存在 f(x)=0 的解\n"); // 因 f(1)=-1, f(3)=1,異號 → 成立
}
延伸範例①:pow()與sqrt()計算畢氏定理
#include <stdio.h>
#include <math.h>
int main(void) {
double a = 3, b = 4;
double c = sqrt(pow(a, 2) + pow(b, 2)); // 斜邊 = √(a² + b²)
printf("%.1f\n", c); // 5.0
return 0;
}
pow(x, y)計算x的y次方,sqrt(x)計算平方根,兩者組合起來就能直接照數學公式寫出畢氏定理。
延伸範例②:fabs()/floor()/ceil()常用數學函式
#include <stdio.h>
#include <math.h>
int main(void) {
printf("%.1f\n", fabs(-7.5)); // 7.5,取絕對值
printf("%.1f\n", floor(3.7)); // 3.0,無條件捨去到整數
printf("%.1f\n", ceil(3.2)); // 4.0,無條件進位到整數
return 0;
}
這三個函式都要#include <math.h>,分別對應「絕對值」、「無條件捨去」、「無條件進位」,跟四捨五入(需自己用+0.5後取整數部分)是不同的觀念。
113專業2Q42 — 答案:A
主題概念:函式/數值方法(勘根定理IVT應用)
解析:這是勘根定理的標準判斷方式:若f(m)與f(n)兩端點函數值異號(或其中一端為0),代表[m,n]內必存在f(x)=0的解,數學表示即為兩端點函數值相乘小於等於0,即f(m)*f(n) <= 0,選(A)。
114專業2Q47 — 答案:A
主題概念:數學函式庫/浮點數運算除錯
解析:x(弧度)從6.28開始每次遞減0.1,範圍大致涵蓋(0, 6.28],橫跨了幾乎一整個週期(0, 2π≈6.2832)。sin(x)在(π, 2π)區間(約3.1416~6.2832)為負值,x的初始值6.28正好落在此區間,sqrt()對負數開根號屬於數學定義域錯誤(domain error),選(A)。
115專業2Q48 — 答案:B
主題概念:函式庫呼叫(數學函式pow/sqrt/fmin/fmax)
解析:pow(X1,Y)=10³=1000;sqrt(X2)=sqrt(10000)=100;exp(Y)=e³≈20.09。(B) fmin(1000,100)=100,格式化輸出"100.00",符合,選(B)。
第12章 流程圖轉程式碼綜合應用
把流程圖轉換成程式碼時,關鍵是分辨流程圖中的判斷框應該對應到 if(只做一次判斷)還是 while(反覆判斷、形成迴圈),並看清楚箭頭「是」與「否」各自指向的方向,決定迴圈條件的真假邏輯。另外,用陣列索引搭配前置/後置遞減來實作「倒序輸出」是常見手法:前置遞減 arr[--i] 會先減再當索引使用,後置遞減 arr[i--] 則是先當索引使用再減,兩者存資料的位置會差一格。
int i = 3;
char buf[4] = {0};
buf[--i] = 'C'; // 前置:i先變2,再存 → buf[2]='C'
buf[--i] = 'B'; // i變1 → buf[1]='B'
buf[--i] = 'A'; // i變0 → buf[0]='A'
printf("%s", buf); // 輸出 "ABC"(存入順序C,B,A,位置卻是0,1,2 → 讀出剛好是輸入的反序)
延伸範例①:用「結束信號」控制的while迴圈(流程圖常見的讀到底再結束)
#include <stdio.h>
int main(void) {
int data[] = {4, 8, 15, 16, 23, -1}; // -1是流程圖中約定的「結束信號」
int i = 0, sum = 0;
while (data[i] != -1) { // 流程圖的判斷框:是否為結束信號?
sum += data[i];
i++;
}
printf("總和 = %d\n", sum); // 66
return 0;
}
很多流程圖用一個特殊值(sentinel)代表「資料輸入完畢」,程式只要不斷判斷是否讀到這個值,就能把流程圖的迴圈邏輯直接轉成while。
延伸範例②:do-while——先做一次,再決定要不要重複
#include <stdio.h>
int main(void) {
int choice[3] = {1, 2, 0}; // 模擬三次選單輸入,0代表離開
int i = 0;
do {
int c = choice[i++];
if (c == 0) { printf("離開選單\n"); break; }
printf("執行選項 %d\n", c);
} while (1);
return 0;
}
流程圖中「先執行動作,再判斷是否重複」的迴圈(例如選單程式)最適合用do-while表示,因為它保證迴圈本體至少會執行一次。
113專業2Q49 — 答案:C
主題概念:迴圈控制/流程圖轉程式碼
解析:流程圖要求「是」時重複詢問並再次判斷,屬於迴圈結構,原程式用if只執行一次,不符合,先排除(A)(B)。迴圈應持續執行的條件是「字數 >= 陣列長度」,直到字數小於陣列長度才跳出,故條件為while(StringLength>=ArrayLength),選(C)。
113專業2Q50 — 答案:C
主題概念:陣列索引/前置與後置遞減運算子
解析:原程式用StringIn[StringLength--](後置遞減),第一次輸入用StringLength=6當索引存入,之後才遞減,導致字元被存到索引6~1,索引0仍是初始值\0,cout輸出時因索引0即為字串結尾符號,會印出空字串。改為前置遞減StringIn[--StringLength]:先遞減再當索引,第1個輸入字元存入索引5,……第6個(最後輸入的字元)存入索引0,cout會從索引0開始印出「最後輸入的字元→…→最先輸入的字元」,恰好是輸入順序的倒序字串,選(C)。
第13章 物件導向程式設計(C++ class)
物件導向三大特性:封裝(Encapsulation)把資料與方法包裝起來、限制外部直接存取;繼承(Inheritance)讓子類別重複使用父類別的程式碼;多型(Polymorphism)讓同一介面在不同物件上有不同表現。class 若未指定存取修飾字,成員預設是 private(只有類別內部能存取);struct 若未指定,成員預設是 public。static 成員是該類別所有物件共享的同一份資料,透過任一物件修改,其它物件看到的值都會一起改變。在類別外定義成員函式時,必須用「回傳型態 類別名稱::函式名稱(參數){...}」的完整格式(範圍解析運算子 ::)。
class Counter {
private: // 未寫修飾字時的預設值就是 private
static int total; // static:所有物件共享同一份
public:
Counter() { total++; }
static int GetTotal() { return total; }
};
int Counter::total = 0; // 類別外初始化 static 成員,須加上 類別名稱::
int main() {
Counter a, b, c;
printf("%d\n", Counter::GetTotal()); // 3(三個物件共享同一份total)
}
延伸範例①:建構子/解構子的呼叫時機
#include <iostream>
class Logger {
public:
Logger() { std::cout << "建構子執行:物件誕生\n"; }
~Logger() { std::cout << "解構子執行:物件銷毀\n"; }
};
int main() {
Logger log1;
std::cout << "main執行中...\n";
return 0; // log1離開作用域時,解構子會自動被呼叫
}
建構子(Constructor)在物件建立的當下自動執行,通常用來初始化成員;解構子(Destructor)則在物件生命週期結束時自動執行,常用來釋放資源。
延伸範例②:繼承與多型(virtual function)
#include <iostream>
class Animal {
public:
virtual void speak() { std::cout << "動物發出聲音\n"; }
};
class Dog : public Animal {
public:
void speak() override { std::cout << "汪汪!\n"; }
};
int main() {
Animal *a = new Dog();
a->speak(); // 多型:雖然a宣告為Animal*,實際呼叫的卻是Dog::speak()
delete a;
return 0;
}
Dog透過: public Animal繼承了Animal的介面;把speak()宣告為virtual後,即使用父類別指標呼叫,實際執行的仍是物件真正的型態(Dog)所覆寫的版本,這就是多型。
111專業2Q49 — 答案:B
主題概念:物件導向程式設計觀念
解析:「資訊隱藏」是「封裝」的作用,而非「多型」的功用;多型指的是同一介面/方法名稱在不同物件上有不同表現方式。(B)將多型與封裝的功能混淆,為錯誤敘述,選(B)。
112專業2Q49 — 答案:A
主題概念:C++類別(class)/成員函式定義/範圍解析運算子(::)
解析:在類別外定義成員函式,必須以「回傳型態 類別名稱::函式名稱(參數){...}」完整格式撰寫。(A)語法正確;(B)缺少CalculateArea::前綴,會被當成全新的全域函式;(C)缺少回傳型態;(D)宣告要回傳double卻沒有return陳述式。選(A)。
112專業2Q50 — 答案:C
主題概念:C++類別與結構(class vs struct)/存取修飾字(private/public/protected)
解析:C++中class若未指定修飾字,成員預設為private,main()中無法直接存取,(A)錯誤。struct型態成員若未指定,預設為public,任何函式只要能取得該struct變數,就能直接存取其成員,(C)正確。選(C)。
113專業2Q45 — 答案:D
主題概念:物件導向/類別與物件(成員存取權限、"."與"->"運算子)
解析:T1、T2皆為Volume類別的「物件」而非指標,存取成員應使用.而非->,(A)(B)語法錯誤。(C) FlowRate是private成員,不能在類別外部直接存取,錯誤。(D) SetOffset是public成員函式,T2.SetOffset(-3.2)語法與存取權限皆正確,選(D)。
113專業2Q46 — 答案:A
主題概念:物件導向/靜態成員(static member)/protected存取權限
解析:Offset宣告為static成員,是該類別所有物件共享的同一份資料,透過任一物件修改,所有物件看到的值都會一起改變,(A)正確。(B) T1以Volume(1.0,2.3)建構,其FlowRate為1.0而非0.0,錯誤。(C) ToatlFlow()宣告在protected區段,在類別外部不能直接呼叫,錯誤。(D) (Item)1等於_Time,GetParameter會回傳Time而非FlowRate,錯誤。選(A)。
115專業2Q47 — 答案:A
主題概念:C++物件導向(建構子Constructor語法)
解析:C++建構子的函式名稱必須與類別名稱完全相同,且不可指定任何回傳型態(連void都不行)。只有(A)符合此語法規則,選(A)。
附錄:年度題號 × 章節對照表
| 年度題號 | 所屬章節 | 年度題號 | 所屬章節 |
|---|---|---|---|
| 111Q36 | 第1章 | 113Q43 | 第9章 |
| 111Q37 | 第1章 | 113Q44 | 第9章 |
| 111Q38 | 第2章 | 113Q45 | 第13章 |
| 111Q39 | 第2章 | 113Q46 | 第13章 |
| 111Q40 | 第3章 | 113Q47 | 第10章 |
| 111Q41 | 第3章 | 113Q48 | 第10章 |
| 111Q42 | 第4章 | 113Q49 | 第12章 |
| 111Q43 | 第5章 | 113Q50 | 第12章 |
| 111Q44 | 第5章 | 114Q35 | 第6章 |
| 111Q45 | 第6章 | 114Q36 | 第6章 |
| 111Q46 | 第6章 | 114Q37 | 第2章 |
| 111Q47 | 第8章 | 114Q38 | 第4章 |
| 111Q48 | 第8章 | 114Q39 | 第2章 |
| 111Q49 | 第13章 | 114Q40 | 第3章 |
| 111Q50 | 第9章 | 114Q41 | 第5章 |
| 112Q36 | 第8章 | 114Q42 | 第6章 |
| 112Q37 | 第1章 | 114Q43 | 第2章 |
| 112Q38 | 第1章 | 114Q44 | 第6章 |
| 112Q39 | 第1章 | 114Q45 | 第3章 |
| 112Q40 | 第8章 | 114Q46 | 第9章 |
| 112Q41 | 第7章 | 114Q47 | 第11章 |
| 112Q42 | 第8章 | 114Q48 | 第3章 |
| 112Q43 | 第2章 | 114Q49 | 第9章 |
| 112Q44 | 第3章 | 114Q50 | 第9章 |
| 112Q45 | 第3章 | 115Q35 | 第5章 |
| 112Q46 | 第5章 | 115Q36 | 第1章 |
| 112Q47 | 第6章 | 115Q37 | 第6章 |
| 112Q48 | 第7章 | 115Q38 | 第6章 |
| 112Q49 | 第13章 | 115Q39 | 第3章 |
| 112Q50 | 第13章 | 115Q40 | 第5章 |
| 113Q35 | 第6章 | 115Q41 | 第5章 |
| 113Q36 | 第8章 | 115Q42 | 第4章 |
| 113Q37 | 第2章 | 115Q43 | 第5章 |
| 113Q38 | 第6章 | 115Q44 | 第3章 |
| 113Q39 | 第4章 | 115Q45 | 第6章 |
| 113Q40 | 第4章 | 115Q46 | 第7章 |
| 113Q41 | 第3章 | 115Q47 | 第13章 |
| 113Q42 | 第11章 | 115Q48 | 第11章 |
| 115Q49 | 第8章 | ||
| 115Q50 | 第9章 |