C語言練習題:指標(C language exercise: Pointer)
🧩 學習脈絡
基礎操作:從宣告、取值開始(練習一~三)。
進階應用:陣列、指標運算、字串處理(練習四~八)。
安全與抽象:空指標檢查、函式指標(練習九~十)。
⚠️ 注意事項
記憶體管理:
malloc()與free()必須成對使用,避免記憶體洩漏。空指標檢查:存取
NULL指標會造成程式崩潰。函式指標:靈活但容易出錯,需確保型別一致。
練習一:基本語法
- 宣告指標、取變數位址、透過指標取值。
- 範例程式展示
&與*的基本用法。
練習一參考解法:
Exercise 1 solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /* Pointer Basic Syntax Author: Holan */ #include <stdio.h> #include <stdlib.h> int main() { int n = 50; // declaration int *ip; // assignment ip = &n; printf("The value of &n:%X\n", &n); printf("The value of n:%i\n", n); printf("The value of &ip:%X\n", &ip); printf("The value of ip:%X\n", ip); printf("The value of *ip:%i\n", *ip); return 0; } |
練習二:動態記憶體配置
- 使用
malloc()配置記憶體,並用free()釋放。 - 強調檢查
NULL以避免配置失敗。
Write a C program to allocate memory.
練習二參考解法:
Exercise 2 solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /* Dynamic memory allocation Author: Holan */ #include <stdio.h> #include <stdlib.h> int main() { int n; int *pI; printf("How many integers? "); scanf("%d", &n); // using malloc to allocate memory pI = (int *) malloc(n*sizeof(int)); if(pI == NULL) // if fail to allocate { printf("Couldn't allocate memory\n"); return 0; // exit } // Releasing the memory allocated by malloc free(pI); return 0; } |
練習三:兩數相乘
- 透過指標存取輸入的兩個整數,並計算乘積。
使用指標的方式,將兩個數字相乘。
Multiplying two numbers with pointers.
練習三參考解法:
Exercise 3 solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /* Multiplying two numbers with pointers Author: Holan */ #include <stdio.h> #include <stdlib.h> int main() { int n1, n2; int *pn1, *pn2, product; printf("Enter num1:"); scanf("%d", &n1); printf("Enter num2:"); scanf("%d", &n2); pn1 = &n1; pn2 = &n2; product = *pn1 * *pn2; printf("The product of %d and %d is %d\n", *pn1, *pn2, product); return 0; } |
練習四:指標與陣列
- 使用指標存取陣列元素,展示
*(p+i)的用法。
使用指標的語法來取得整數陣列的元素。
練習四參考解法:
Exercise 4 solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* Pointer and array Author: Holan */ #include <stdio.h> #include <stdlib.h> #define N 5 int main() { int a[N] = { 2, 3, 5, 7, 11}; int *pI = a; for(int i = 0; i < N; i++) printf("a[%d] = %d\t", i, *(pI + i)); return 0; } |
練習五:指標運算
- 練習指標遞增與遞減,等同於陣列索引存取。
在練習四時,使用到指標與整數相加的運算。而本題請使用遞增與遞減來達成與練習四一樣的功能。
In exercise 4, the operation: "a pointer plus a integer" is used. Could we use pointer increment and decrement to do exercise 4?
這篇連結文章可以幫助瞭解指標運算。
Here is a tutorial: "How pointer arithmetic works?"
練習五參考解法:
Exercise 5 solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /* Pointer Arithmetic: increment and decrement Author: Holan */ #include <stdio.h> #include <stdlib.h> #define N 5 int main() { int a[N] = { 2, 3, 5, 7, 11}; int *pI = a; printf("Pointer increment:"); // pointer increment for(int i = 0; i < N; i++) printf("a[%d] = %d\t", i, *(pI++)); printf("\n\nPointer decrement:"); // pointer decrement for(int i = N - 1; i >= 0; i--) printf("a[%d] = %d\t", i, *(--pI)); return 0; } |
練習六:字串長度
- 使用指標遍歷字串直到
'\0',計算字串長度。
使用指標語法來求出所輸入字串的長度。
Using pointer to calculate the length of a user input string.
練習六參考解法:
Exercise 6 solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /* Length of a string Author: Holan */ #include <stdio.h> #include <stdlib.h> int main() { char str[100]; int len = 0; printf("Enter a string:"); // Input a string with whitespaces scanf("%[^\n]s", str); char *pC = str; while(*pC != '\0') { len++; pC++; } printf("The length of the given string[%s] is:%d", str, len); return 0; } |
- 透過 call by reference 設計
swap()函式,交換兩個數值。
使用 call by reference 的方式,設計一個可以交換兩數的函式。
Using call by reference to design a function that swaps two numbers.
練習七參考解法:
Exercise 7 solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | /* Swap two numbers Author: Holan */ #include <stdio.h> #include <stdlib.h> void swapV(double *x, double *y); int main() { double n1, n2; printf("Enter number 1:"); scanf("%lf", &n1); printf("Enter number 2:"); scanf("%lf", &n2); printf("Before swap n1:%lf, n2:%lf", n1, n2); swapV(&n1, &n2); printf("\nAfter swap n1:%f, n2:%f", n1, n2); return 0; } void swapV(double *x, double *y) { double t = *x; *x = *y; *y = t; } |
- 使用指標將兩字串合併到第三個字串中。
使用指標語法來合併兩字串。
Using pointer to concatenate two strings.
練習八參考解法:
Exercise 8 solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | /* String concatenation Author: Holan */ #include <stdio.h> #include <stdlib.h> #define LEN 256 int main() { char str1[LEN]; char str2[LEN]; char str3[LEN*2]; printf("Enter str1:"); scanf("%[^\n]%*c", str1); printf("Enter str2:"); scanf("%[^\n]%*c", str2); char *pC = str1; char *pStr = str3; while(*pC) { *pStr = *pC; pC++; pStr++; } pC = str2; while(*pC) { *pStr = *pC; pC++; pStr++; } // end with '\0' *pStr = '\0'; printf("Str1:%s, Str2:%s, Str3:%s", str1, str2, str3); return 0; } |
Please refer to this tutorial: NULL pointer in C.
Design a C program to practice the null pointer syntax.
練習九參考解法:
Exercise 9 solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /* NULL pointer Author: Holan */ #include <stdio.h> #include <stdlib.h> int main() { int *iP = NULL; if(iP == NULL) { printf("The value of iP is %u", iP); // Couldn't access to *iP // The following code will crush printf("The value of *iP is %d", *iP); } else { printf("The value of iP is %u", iP); printf("The value of *iP is %d", *iP); } return 0; } |
- 使用函式指標呼叫加、減、乘、除等運算函式。
Please refer to this tutorial: Function pointers in C.
Design a C program to practice function pointer. For example, addition, subtraction, multiplication and division of two numbers.
練習十參考解法:
Exercise 10 solution:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /* Function pointers Author: Holan */ #include <stdio.h> #include <stdlib.h> double add(double n1, double n2); double sub(double n1, double n2); double mul(double n1, double n2); double divi(double n1, double n2); int main() { double a = 3.44, b = 9.999; double (*op)(double, double) = add; printf("The addition of %f and %f is %f\n", a, b, op(a, b)); op = sub; printf("The subtraction of %f and %f is %f\n", a, b, op(a, b)); op = mul; printf("The multiplication of %f and %f is %f\n", a, b, op(a, b)); op = divi; printf("The division of %f and %f is %f\n", a, b, op(a, b)); return 0; } double add(double a, double b) { return a + b;} double sub(double a, double b) { return a - b;} double mul(double a, double b) { return a * b;} double divi(double a, double b) { return a / b;} |
程式碼盲點補充與延伸說明:
練習一:基本語法(宣告、取址、取值)
原文精要:呈現基本語法,使用
&n取得變數n的記憶體位址,並用int *ip宣告指標來儲存該位址;再透過*ip(解引用)取得該位址所存放的數值。進階解析與盲點補充:
格式化字串的選擇:原文使用
%X(十六進位)來印出記憶體位址。但在現代標準 C 語言中,印出指標位址最標準、最安全的作法是使用%p,並且將其強制轉型為(void *)。因為指標的長度在 32 位元和 64 位元系統下不同,%p能保證正確的輸出格式。*符號的多重身份:int *ip;裡的*代表型態宣告(這是一個指向整數的指標)。*ip裡的*代表運算子(解引用 / Dereference),意思是「去拿這個位址裡面裝的值」。
練習二:動態記憶體配置(malloc 與 free)
原文精要:使用者輸入數量
n,利用malloc(n * sizeof(int))在堆疊(Heap)動態要一塊空間,最後用free()釋放。進階解析與盲點補充:
記憶體洩漏(Memory Leak):只要用了
malloc(),就必須在不需要時呼叫free(),否則該記憶體會一直被佔用直到程式結束。安全性檢查:原文有寫到
if(pI == NULL)的檢查,這是非常優良的習慣!因為當系統記憶體不足時,malloc會返回NULL。懸空指標(Dangling Pointer):在執行
free(pI);之後,pI依然指向原本的那個記憶體位址,但那塊位址已經不能用了。安全的實務作法是在free(pI);後,緊接著補上一句pI = NULL;,避免後續不小心誤用。
練習三:兩數相乘
原文精要:透過兩個指標
pn1與pn2分別指向n1、n2,然後透過*pn1 * *pn2計算乘積。進階解析與盲點補充:
可讀性陷阱:
product = *pn1 * *pn2;這行程式碼在編譯器眼中是完全合法的(第一個*是解引用,第二個*是乘號,第三個*是解引用)。但是對於人類閱讀來說容易混淆。在團隊開發或考試中,建議寫成product = (*pn1) * (*pn2);,加上括號可讀性會大幅提升。
練習四:指標與陣列
原文精要:陣列名稱本身就是一個指向陣列第一個元素的常數指標。原文設定
int *pI = a;,並用*(pI + i)來走訪陣列。進階解析與盲點補充:
等價關係:在 C 語言中,
a[i]其實完全等價於*(a + i)。當我們寫
*(pI + i)時,底層並不是單純的「位址數字加i」。編譯器會根據指標的型態(這裡是int,佔 4 bytes),自動將其轉換為位址 + (i * 4)。這就是指標的「步進(Stride)」概念。
練習五:指標運算(遞增與遞減)
原文精要:利用
*(pI++)和*(--pI)達到前進與後退走訪陣列的效果。進階解析與盲點補充:
算符優先權(極重要):
*(pI++)包含了解引用*和後置遞增++。後置遞增
++的優先權高於*,但它是「先取值、後加 1」。所以
*(pI++)的運作邏輯是:先取出當前pI指向的值,接著pI指標往後移動一個單位(指向下一個元素)。
原文在後半段倒序印出時,使用
*(--pI)。因為前進迴圈結束後,pI已經指向了陣列界限外的下一個位置(即a[5]的位置),所以必須先「前置遞減--」讓指標退回a[4],再進行解引用。
練習六:字串長度
原文精要:字串在 C 語言中是以
\0(Null terminator)結尾的字元陣列。透過while(*pC != '\0')讓指標逐字元往後移,直到遇到終止符。進階解析與盲點補充:
scanf技巧:原文用了scanf("%[^\n]s", str);,這是一個進階的格式化輸入法,代表「讀取直到遇見換行符號為止」,這樣可以讀入包含空格的句子,比單純的%s更實用。精簡寫法:因為
\0的 ASCII 碼就是0(布林值為偽),所以while(*pC != '\0')在實務上常常被簡寫為更漂亮的while(*pC)。
練習七:交換兩數(傳址呼叫 Call by Reference / Pointer)
原文精要:C 語言本質上只有 Call by value。若要在函式內部改變外部變數的值,必須傳遞該變數的「記憶體位址」,即
swapV(&n1, &n2);。進階解析與盲點補充:
如果在函式內寫
void swap(int x, int y),裡面做交換只會換到區域變數(副本),外面的n1, n2毫無影響。透過傳遞指標
void swapV(double *x, double *y),函式內的*x和*y就直接代表了外面的n1和n2,這才能真正成功交換數值。
練習八:字串合併
原文精要:不使用標準庫
<string.h>的strcat,而是手動用指標將str1與str2的字元依序複製到str3中。進階解析與盲點補充:
最關鍵的一步:原文最後加上了
*pStr = '\0';。這是手動操作字串絕對不能忘記的步驟!如果沒有手動補上字串結束符,printf在印出str3時就會因為找不到結尾而繼續往後讀取亂碼,甚至導致程式崩潰。緩衝區溢位風險:
str3的大小宣告為LEN*2。實務上必須確保str1長度 +str2長度小於LEN*2,否則會發生記憶體覆蓋的危險。
練習九:空指標(NULL Pointer)
原文精要:展示宣告
int *iP = NULL;。當指標為NULL時,絕對不可以進行解引用(*iP),否則程式會直接崩潰(Segmentation Fault)。進階解析與盲點補充:
安全防護網:空指標代表「該指標目前沒有指向任何合法的記憶體空間」。在實際開發中,任何接收指標的函式,開頭第一步通常都是檢查指標是否為
NULL(如:assert(p != NULL);或if(p == NULL) return;),這能過濾掉 80% 以上的執行期錯誤。
練習十:函式指標(Function Pointer)
原文精要:指標不僅能指向數據,還能指向「程式碼所在的記憶體位址」(即函式)。原文宣告了
double (*op)(double, double) = add;,並透過切換op來執行不同的數學運算。進階解析與盲點補充:
語法拆解:
double (*op)(double, double)(*op):這是一個指標,名字叫op。後面的
(double, double):它指向的函式必須接收兩個double參數。最前面的
double:它指向的函式必須回傳double。注意:括號不可省略。如果寫成
double *op(double, double),會變成「宣告一個名為op的函式,它回傳一個double *指標」。
實務應用:函式指標是 C 語言實現「多型(Polymorphism)」和「回呼函式(Callback Function)」的核心。例如標準庫的快速排序
qsort(),就是透過傳入函式指標來決定排序是升冪還是降冪。
若您覺得文章寫得不錯,請點選文章上的廣告,來支持小編,謝謝。
If you like this post, please click the ads on the blog or buy me a coffee. Thank you very much.
留言