C 語言程式設計教學:陣列和指標

PUBLISHED ON JUL 19, 2018 — PROGRAMMING

指標和陣列的關係相當密切,時常會讓我們以為兩者可相互交換,但兩者仍有些不同。

相似之處

指標可以存取陣列的元素:

#include <assert.h>
#include <stddef.h>

int main(void) {
    int arr[] = {4, 2, 3, 5, 1};
    
    // Same as int *p = &arr[0];
    int *i_p = arr;
    
    for (size_t i = 0; i < 5; i++) {
        assert(*i_p == arr[i]);
        
        // Go to the next element of arr.
        i_p++;
    }
    
    return 0;
}

在本例中,i_p 指向 arr 第 1 個元素所在的位置。每次 i_p 遞增 1 時,會指向 arr 的下一個位置。這裡利用到指標運算的特性。

我們也可以用指標動態配置一塊陣列:

#include <assert.h>
#include <stddef.h>
#include <stdlib.h>

int main(void) {
    int *i_p = calloc(5, sizeof(int));
    if (!i_p) {
        return EXIT_FAILURE;
    }
    
    int arr[] = {4, 2, 3, 5, 1};
    
    for (size_t i = 0; i < 5; i++) {
        i_p[i] = arr[i];
    }
    
    for (size_t i = 0; i < 5; i++) {
        assert(i_p[i] == arr[i]);
    }
    
    // Mutate i_p[1]
    i_p[1] = 99;
    assert(i_p[1] == 99);
    
    free(i_p);
    
    return 0;
}

在本例中,我們用 calloc 而非 malloc 因為前者可以一併配置記憶體和初始化 i_p。之後的操作其實和一般陣列幾乎沒有兩樣,讀者可自行閱讀相關程式碼。由於 i_p 是從 heap 動態配置的,最後別忘了要釋放記憶體。

相異之處

指標和陣列還是有些不同。像是先前用來估陣列大小的方式在動態配置的陣列會失敗:

#include <assert.h>
#include <stddef.h>
#include <stdlib.h>

int main()
{
    int *i_p = malloc(sizeof(int) * 5);
    if (!i_p) {
        return EXIT_FAILURE;
    }
    
    // Try to get the size of *i_p.
    size_t sz = sizeof(*i_p) / sizeof(int);
    
    // Failed.
    assert(sz == 5);
    
    free(i_p);
    
    return 0;
}

我們也不能把陣列變數當指標來運算:

#include <stddef.h>
#include <stdio.h>

int main(void) {
    int arr[] = {4, 2, 3, 5, 1};
    
    for (size_t i = 0; i < 5; i++) {
        printf("%d ", *arr);
        
        // Error.
        arr++;
    }
    printf("\n");
    
    return 0;
}

動態配置多維陣列

我們先前已經看過動態配置一維陣列的方法,我們這裡來看如何動態配置多維陣列。動態配置多維陣列的方式有兩種:

  • 陣列的陣列 (array of array)
  • 將多維陣列轉為一維陣列

第一種方式比較直觀,可見下例:

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>

int main(void) {
    size_t c = 3;  // Column.
    size_t r = 2;  // Row.
    
    // Allocate outer layer of mtx.
    double **mtx = malloc(r * sizeof(double *));
    if (!mtx) {
        return EXIT_FAILURE;
    }
    
    // Allocate inner layer of mtx.
    for (size_t i = 0; i < r; i++) {