C語言練習題:指標(C language exercise: Pointer)

Pointer concepts:
2. Introduction to C Pointers

🧩 學習脈絡

  1. 基礎操作:從宣告、取值開始(練習一~三)。

  2. 進階應用:陣列、指標運算、字串處理(練習四~八)。

  3. 安全與抽象:空指標檢查、函式指標(練習九~十)。

⚠️ 注意事項

  • 記憶體管理malloc()free() 必須成對使用,避免記憶體洩漏。

  • 空指標檢查:存取 NULL 指標會造成程式崩潰。

  • 函式指標:靈活但容易出錯,需確保型別一致。


練習一:基本語法
  • 宣告指標、取變數位址、透過指標取值。
  • 範例程式展示 &* 的基本用法。
設計一個C語言程式來呈現指標的語法,例如宣告、取址、取值等。

Exercise 1: Basic Syntax
Design a C program to demonstrate the basic syntax of pointer. Such as declaration, address and value. 

練習一參考解法:
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 以避免配置失敗。

撰寫用 malloc()free() 來配置記憶體的程式。

Exercise 2: Dynamic Memory Allocation
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;
}


練習三:兩數相乘
  • 透過指標存取輸入的兩個整數,並計算乘積。

使用指標的方式,將兩個數字相乘。

Exercise 3: Multiplying two numbers
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: Pointer and array
Using a pointer to access the elements of an integer array.

練習四參考解法:
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;
}

練習五:指標運算
  • 練習指標遞增與遞減,等同於陣列索引存取。

在練習四時,使用到指標與整數相加的運算。而本題請使用遞增與遞減來達成與練習四一樣的功能。

Exercise 5: Pointer Arithmetic
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',計算字串長度。

使用指標語法來求出所輸入字串的長度。

Exercise 6: The length of a string
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 的方式,設計一個可以交換兩數的函式。

Exercise 7: Swap two numbers
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;
}

練習八:字串合併
  • 使用指標將兩字串合併到第三個字串中。

使用指標語法來合併兩字串。

Exercise 8: String concatenation
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;
}

練習九:空指標
  • 練習 NULL 指標,理解存取空指標會造成程式錯誤。

常見的觀念可參考「空指標與NULL」。
請撰寫一程式來練習空指標的語法。

Exercise 9: null pointer 
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;
}

練習十:函式指標
  • 使用函式指標呼叫加、減、乘、除等運算函式。

可參考此篇文章:[C語言] function pointer介紹來了解 function pointer。
請設計一程式來練習 function pointer,例如兩數字的加、減、乘、除等運算。

Exercise 10: Function Pointer
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(解引用)取得該位址所存放的數值。

  • 進階解析與盲點補充

    1. 格式化字串的選擇:原文使用 %X(十六進位)來印出記憶體位址。但在現代標準 C 語言中,印出指標位址最標準、最安全的作法是使用 %p,並且將其強制轉型為 (void *)。因為指標的長度在 32 位元和 64 位元系統下不同,%p 能保證正確的輸出格式。

    2. * 符號的多重身份

      • int *ip; 裡的 * 代表型態宣告(這是一個指向整數的指標)。

      • *ip 裡的 * 代表運算子(解引用 / Dereference),意思是「去拿這個位址裡面裝的值」。

練習二:動態記憶體配置(mallocfree

  • 原文精要:使用者輸入數量 n,利用 malloc(n * sizeof(int)) 在堆疊(Heap)動態要一塊空間,最後用 free() 釋放。

  • 進階解析與盲點補充

    1. 記憶體洩漏(Memory Leak):只要用了 malloc(),就必須在不需要時呼叫 free(),否則該記憶體會一直被佔用直到程式結束。

    2. 安全性檢查:原文有寫到 if(pI == NULL) 的檢查,這是非常優良的習慣!因為當系統記憶體不足時,malloc 會返回 NULL

    3. 懸空指標(Dangling Pointer):在執行 free(pI); 之後,pI 依然指向原本的那個記憶體位址,但那塊位址已經不能用了。安全的實務作法是在 free(pI); 後,緊接著補上一句 pI = NULL;,避免後續不小心誤用。

練習三:兩數相乘

  • 原文精要:透過兩個指標 pn1pn2 分別指向 n1n2,然後透過 *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') 讓指標逐字元往後移,直到遇到終止符。

  • 進階解析與盲點補充

    1. scanf 技巧:原文用了 scanf("%[^\n]s", str);,這是一個進階的格式化輸入法,代表「讀取直到遇見換行符號為止」,這樣可以讀入包含空格的句子,比單純的 %s 更實用。

    2. 精簡寫法:因為 \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 就直接代表了外面的 n1n2,這才能真正成功交換數值。

練習八:字串合併

  • 原文精要:不使用標準庫 <string.h>strcat,而是手動用指標將 str1str2 的字元依序複製到 str3 中。

  • 進階解析與盲點補充

    1. 最關鍵的一步:原文最後加上了 *pStr = '\0';。這是手動操作字串絕對不能忘記的步驟!如果沒有手動補上字串結束符,printf 在印出 str3 時就會因為找不到結尾而繼續往後讀取亂碼,甚至導致程式崩潰。

    2. 緩衝區溢位風險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 來執行不同的數學運算。

  • 進階解析與盲點補充

    1. 語法拆解double (*op)(double, double)

      • (*op):這是一個指標,名字叫 op

      • 後面的 (double, double):它指向的函式必須接收兩個 double 參數。

      • 最前面的 double:它指向的函式必須回傳 double

      • 注意:括號不可省略。如果寫成 double *op(double, double),會變成「宣告一個名為 op 的函式,它回傳一個 double * 指標」。

    2. 實務應用:函式指標是 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.

留言

這個網誌中的熱門文章