Back
Clang & Wasm 教學

陣列與指標

如同城市規劃師掌握土地配置與交通動線,透過陣列與指標精準控制記憶體空間與資料流向,打造高效運作的程式系統。


目標

1. 陣列的基本概念

1.1 什麼是陣列?

  • 陣列是一組相同資料類型的變數集合
  • 類似宿舍的置物櫃,每個櫃子編號從0開始
  • 宣告語法:
    資料類型 陣列名稱[元素數量];
    
    範例:
    int scores[5];       // 可儲存5個整數
    float temps[24];     // 可儲存24個浮點數
    

1.2 陣列初始化

  • 靜態初始化:宣告時直接賦值
    int primes[] = {2, 3, 5, 7, 11};  // 自動推算長度
    char vowels[5] = {'a', 'e', 'i', 'o', 'u'};
    
  • 動態初始化:後續賦值
    scores[0] = 90;  // 第一個元素
    scores[1] = 85;  // 第二個元素
    

2. 陣列與記憶體

2.1 記憶體連續性

  • 陣列元素在記憶體中是連續儲存
  • 可用&運算子查看位址:
    printf("primes[0]位址: %p\n", &primes[0]);
    printf("primes[1]位址: %p\n", &primes[1]);
    
    輸出範例:
    primes[0]位址: 0x7ffd4a3b2f10
    primes[1]位址: 0x7ffd4a3b2f14  // 相差4 bytes(int大小)
    

2.2 sizeof運算子

  • 計算陣列總記憶體佔用:
    printf("primes陣列大小: %zu bytes\n", sizeof(primes));
    
  • 計算單個元素大小:
    printf("每個元素大小: %zu bytes\n", sizeof(primes[0]));
    

3. 陣列實作練習

3.1 基本操作

// 計算陣列總和
int numbers[] = {10, 20, 30, 40, 50};
int sum = 0;

for (int i = 0; i < 5; i++) {
    sum += numbers[i];
}
printf("總和: %d\n", sum);

4. 指標的基本概念

4.1 什麼是指標?

  • 指標是儲存記憶體位址的變數
  • 透過指標可以直接操作記憶體中的資料
  • 宣告語法:
    資料類型* 指標變數名稱;
    
    範例:
    int* ptr;      // 指向整數的指標
    float* fptr;   // 指向浮點數的指標
    

4.2 指標運算子

  • & (取址運算子):取得變數的記憶體位址
    int num = 10;
    int* ptr = &num;  // ptr現在儲存num的位址
    
  • ***** (取值運算子):取得指標指向的值
    printf("num的值: %d\n", *ptr);  // 輸出10
    

5. 指標與陣列的關係

5.1 陣列名稱的本質

  • 陣列名稱本身就是指向第一個元素的指標
    int arr[3] = {10, 20, 30};
    printf("%p == %p\n", arr, &arr[0]);  // 兩者相同
    
  • 可以用指標方式存取陣列元素:
    printf("arr[1]: %d\n", *(arr + 1));  // 輸出20
    

5.2 指標算術運算

  • 指標加減整數會根據資料類型自動調整位址
    int* ptr = arr;
    printf("ptr: %p\n", ptr);      // 指向arr[0]
    printf("ptr+1: %p\n", ptr+1);  // 指向arr[1]
    

6. 陣列作為函數參數

6.1 傳遞陣列給函數

  • 函數參數可以使用指標或陣列形式宣告
    // 兩種等價的宣告方式
    void printArray(int* arr, int size);
    void printArray(int arr[], int size);
    
  • 實際呼叫範例:
    void printArray(int* arr, int size) {
        for(int i=0; i<size; i++) {
            printf("%d ", arr[i]);
        }
    }
    
    int main() {
        int nums[] = {1, 2, 3, 4, 5};
        printArray(nums, 5);  // 傳入陣列名稱和大小
    }
    

6.2 修改陣列內容

  • 函數內透過指標可直接修改原始陣列:
    void squareArray(int* arr, int size) {
        for(int i=0; i<size; i++) {
            arr[i] = arr[i] * arr[i];
        }
    }
    

7. 綜合實作練習

7.1 指標操作練習

int nums[] = {10, 20, 30, 40, 50};
int* ptr = nums;

// 請試試看以下運算結果:
printf("%d\n", *ptr);       // 輸出?
printf("%d\n", *(ptr+2));   // 輸出?
printf("%d\n", ptr[3]);     // 輸出?

9. 動態記憶體管理

9.1 靜態 vs 動態分配

  • 靜態分配:編譯時決定大小(傳統陣列)
    int arr[100];  // 固定大小
    
  • 動態分配:執行時決定大小
    int* arr = malloc(100 * sizeof(int));  // 可調整大小
    

9.2 記憶體管理函數

  • malloc:分配未初始化記憶體
    int* nums = malloc(5 * sizeof(int));  // 分配5個int空間
    
  • calloc:分配並初始化為0
    int* zeros = calloc(5, sizeof(int));  // 5個int全部設0
    
  • free:釋放記憶體(重要!)
    free(nums);  // 釋放記憶體
    nums = NULL; // 避免懸吊指標
    

10. 動態陣列實作

10.1 建立動態陣列

int size;
printf("輸入陣列大小: ");
scanf("%d", &size);

int* dynArr = malloc(size * sizeof(int));
for(int i=0; i<size; i++) {
    dynArr[i] = i*10;  // 初始化值
}

10.2 調整陣列大小

int newSize = size * 2;
int* temp = realloc(dynArr, newSize * sizeof(int));
if(temp != NULL) {
    dynArr = temp;  // 擴容成功
    for(int i=size; i<newSize; i++) {
        dynArr[i] = 0;  // 初始化新增空間
    }
}

11.6 指標作為函數參數的簡單應用

11.6.1 基本概念(與scanf類比)

// scanf 就是最典型的指標參數應用
int num;
scanf("%d", &num);  // 傳遞num的地址讓scanf能修改它的值

// 同理,我們可以自訂這樣的函數
void set_value(int* ptr) {
    *ptr = 100;  // 透過指標修改外部變數
}

int main() {
    int a = 0;
    set_value(&a);  // 類似scanf的用法
    printf("%d", a); // 輸出100
}

11.6.2 陣列修改函數

// 像scanf修改變數一樣修改陣列元素
void set_array_element(int* arr, int index) {
    printf("請輸入新值: ");
    scanf("%d", &arr[index]);  // 類似scanf(&變數)的用法
}

int main() {
    int nums[5] = {0};
    set_array_element(nums, 2);  // 修改nums[2]
    printf("新值: %d", nums[2]);
}

11.6.3 簡易交換函數

// 像scanf需要&一樣,交換函數也需要指標參數
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 3, y = 5;
    swap(&x, &y);  // 傳地址才能修改原始變數
    printf("x=%d, y=%d", x, y); // 輸出x=5, y=3
}

11.7 重點提示

  • 記住scanf需要&的原理
  • 當函數需要修改原始變數時,就要傳指標
  • 陣列名稱本身就是指標,所以不需要&
  • 這種用法讓函數能真正修改外部變數,而不只是複本

12. 多維陣列實作

12.1 靜態二維陣列

// 宣告3x4的整數矩陣
int matrix[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

// 存取元素
printf("%d", matrix[1][2]); // 輸出7

12.2 動態二維陣列

int rows = 3, cols = 4;
int** matrix = malloc(rows * sizeof(int*));
for(int i=0; i<rows; i++) {
    matrix[i] = malloc(cols * sizeof(int));
}

// 初始化
for(int i=0; i<rows; i++) {
    for(int j=0; j<cols; j++) {
        matrix[i][j] = i*cols + j + 1;
    }
}

// 釋放記憶體
for(int i=0; i<rows; i++) {
    free(matrix[i]);
}
free(matrix);

12.3 三維陣列實作

// 靜態宣告
int cube[2][3][4] = {
    {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}},
    {{13,14,15,16}, {17,18,19,20}, {21,22,23,24}}
};

// 動態宣告
int x=2, y=3, z=4;
int*** cube = malloc(x * sizeof(int**));
for(int i=0; i<x; i++) {
    cube[i] = malloc(y * sizeof(int*));
    for(int j=0; j<y; j++) {
        cube[i][j] = malloc(z * sizeof(int));
    }
}

12.4 矩陣運算範例

// 矩陣相加
void matrix_add(int** a, int** b, int** result, int rows, int cols) {
    for(int i=0; i<rows; i++) {
        for(int j=0; j<cols; j++) {
            result[i][j] = a[i][j] + b[i][j];
        }
    }
}

// 矩陣轉置
void matrix_transpose(int** src, int** dst, int rows, int cols) {
    for(int i=0; i<rows; i++) {
        for(int j=0; j<cols; j++) {
            dst[j][i] = src[i][j];
        }
    }
}

12.5 記憶體連續分配優化

// 單次分配所有記憶體
int** create_matrix(int rows, int cols) {
    int** mat = malloc(rows * sizeof(int*));
    int* data = malloc(rows * cols * sizeof(int));
    
    for(int i=0; i<rows; i++) {
        mat[i] = &data[i * cols];
    }
    return mat;
}

void free_matrix(int** mat) {
    free(mat[0]); // 釋放資料區塊
    free(mat);    // 釋放指標陣列
}

課堂作業:陣列與指標函數實作「成績分析器」

作業目標

  1. 使用陣列儲存學生成績
  2. 用指標傳遞陣列給函數
  3. 實作常見統計函數 a. 平均分數 b. 分級人數 (059, 6069, 7079, 8089, 90~100)

核心任務

// 函數原型宣告
float calculateAverage(float* ptr, int n); // 計算平均
void analyzeScores(float* ptr, int n, int* level);    // 分析成績分佈

程式結構

#include <stdio.h>

int main() {
    int num;
    printf("請輸入學生人數:");
    scanf("%d", &num);
    
    float scores[num]; // 宣告變長陣列
    int level[5] = { 0 }; // 
    // 透過指標傳遞陣列
    inputScores(scores, num);
    
    // 計算並顯示平均
    float avg = calculateAverage(scores, num);
    printf("\n平均成績:%.2f\n", avg);
    
    // 分析成績分佈
    analyzeScores(scores, num);
    
    return 0;
}

執行範例

請輸入學生人數:5
請輸入5位學生成績:
學生1: 88
學生2: 72
學生3: 95
學生4: 63
學生5: 77

平均成績:79.00

成績分佈:
0~59分: 0人
60~69分: 1人
70~79分: 2人
80~89分: 1人
90~100分: 1人
SNPQ © 2025