本文繼續《C語言程式開發經典實例》系列,精選了第 71 至 80 號共十個經典範例。涵蓋結構體陣列處理、單向鏈表的建立與反轉、兩表串接、指標函數應用、指標的指標操作、邏輯謎題求解、分數區間獎金計算,以及著名的「五猴分桃」數學難題。每個範例均附完整原始碼與詳細註解,適合初學者鞏固語法,也供進階開發者參考演算法思路。
編寫 input() 與 output() 兩個函式,分別完成 5 位學生學號、姓名及三科成績的鍵盤輸入與格式化輸出。
student,包含字元陣列 num[6]、name[8] 及整數陣列 score[4](索引 1~3 存三科分數)。input() 採雙層迴圈逐筆讀取,scanf("%s") 讀字串,scanf("%d") 讀整數。print() 使用 %-6s、%-10s、%-8d 靠左對齊輸出,形成整齊表格。#define N 5
struct student {
char num[6];
char name[8];
int score[4];
} stu[N];
void input(struct student stu[]) {
int i, j;
for (i = 0; i < N; i++) {
printf("\n please input %d of %d\n", i+1, N);
printf("num: "); scanf("%s", stu[i].num);
printf("name: "); scanf("%s", stu[i].name);
for (j = 0; j < 3; j++) {
printf("score %d: ", j+1);
scanf("%d", &stu[i].score[j]);
}
}
}
void print(struct student stu[]) {
int i, j;
printf("\nNo. Name Sco1 Sco2 Sco3\n");
for (i = 0; i < N; i++) {
printf("%-6s%-10s", stu[i].num, stu[i].name);
for (j = 0; j < 3; j++)
printf("%-8d", stu[i].score[j]);
printf("\n");
}
}
int main() {
input(stu);
print(stu);
return 0;
}
head 指向鏈表頭。next 設為 NULL。原始碼中 ptr=head; 應在 malloc 之後,否則會丟失首節點位址;列印迴圈裡的 tr=ptr->next; 疑為筆誤,應為 ptr=ptr->next;。
每讀一筆資料,即配置新節點插在現有鏈表頭部(head->next = ptr; ptr = head;),輸入順序 1→2→3→4→5,鏈表實際順序即為 5→4→3→2→1,遍歷即得反向輸出,無需額外反轉演算法。
create_list():由陣列建立鏈表,回傳頭指標。concatenate():尋找第一個鏈表尾節點,將其 next 指向第二個鏈表頭。selection_sort():在鏈表上實作選擇排序,配合 delete_node() 刪除已排序的最小節點並輸出。此範例展示如何將複雜操作拆解為可重用函式,是結構化程式設計的優良示範。
四人各說一句話,只有其中一人說真話,其餘說謊。利用布林邏輯累加真話計數 n,當 n==3 時輸出對應字元(ASCII 65='A' 起),揭曉說真話者。
int main() {
int i, n;
for (i = 1; i < 5; i++) {
n = 0;
if (i != 1) n++;
if (i == 3) n++;
if (i == 4) n++;
if (i != 4) n++;
if (n == 3)
printf("說真話的是: %c\n", 64 + i);
}
return 0;
}
執行結果輸出 C,即第三人說真話。
輸入 n,若為偶數計算 1/2+1/4+...+1/n,奇數計算 1/1+1/3+...+1/n。要求使用函數指標回呼機制。
float (*fp)(int)。dcall(fp, n) 作為通用呼叫介面,執行 return (*fp)(n);。peven()、podd() 分別實作偶數、奇數項求和,注意 1/(float)i 強制浮點除法。已知 char *s[] = {"man","woman","girl","boy","sister"}; 及 char **q;,請在迴圈填入敘述使其依序列印五個字串。
for (k = 0; k < 5; k++) {
q = &s[k]; // 或 q = s + k;
printf("%s\n", *q);
}
s[k] 型別為 char*,取址得 char** 指派給 q,解參考 *q 即得字串首址。
if (m < p->age)
q = p++; // 錯誤:後置++ 導致 q 指向舊位址,m 卻取新位址的 age
m = q->age;
if (m < p->age) {
m = p->age;
q = p;
}
p++;
或直接用索引 person[i] 避免指標運算錯誤。
swap() 內部宣告 char *p[20]; 做暫存,但 strcpy(p, p1); 將字串複製到未初始化的指標陣列,導致未定義行為。
void swap(char **p1, char **p2) {
char *tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
主程式改呼叫 swap(&str1, &str2);,真正交換指標指向,而非搬移字串內容,效率更高且安全。
設原始桃子數為 x,每猴操作:x = (x-1)/5 * 4,需滿足 5 次運算後皆為整數。反向推導:若第 5 猴操作前剩 y,則 y = 4*k,上一輪剩 5*k+1。程式從 4 開始以 4 為步長試探,模擬 5 輪逆運算,首個通過即為最小解。
int main() {
long x = 4;
while (1) {
long cur = x;
int ok = 1;
for (int i = 0; i < 5; i++) {
if (cur % 4 != 0) { ok = 0; break; }
cur = cur / 4 * 5 + 1; // 逆推上一輪剩餘
}
if (ok) { printf("最少桃子數: %ld\n", cur); break; }
x += 4;
}
return 0;
}
答案為 3121 顆。
本集錦涵蓋資料結構、指標進階、函數指標、邏輯推理、數學建模等核心能力。建議讀者:
swap 改為泛型 void swap(void**, void**) 練習泛型程式設計。下一集將介紹檔案操作、位元運算與巨集進階應用,敬請期待。