C/C++:pointer to array 以及 array of pointers 的差異

這篇文章簡略紀錄一下 C/C++ pointer to array 和 array of pointers 的差異。從字面上理解兩者的差異很容易,pointer to array 即為指向陣列的指標,而 array of pointers 就是一個含有指標的陣列。雖然字面上的意義很好理解,並且在 C/C++ 的世界裡兩者的語法非常接近,但他們卻有天壤之別的差異,而且我發現很多 C/C++ 學習者並沒有搞清楚兩者的區別,所以將在這篇文章簡介 C/C++ 裡面 pointer to array 和 array of pointers 的差別以及使用方式。

Array of pointers 與 Pointer to array 的語法差異

我們先從兩者的語法差異開始,舉例來說,如果想要宣告一個包含 4 個 int 指標的陣列,以及一個指標指向長度為 4 的 int 陣列,各自的語法如下:

int *array[4]; // array of pointers
int (*ptr)[4]; // pointer to array

兩者在語法上的宣告方式很像,只差在有無 () 而已,但小小的差異就會造成特性上的不同。因為 array 是一個陣列,並且包含 4 個 int 指標,可以分別指向不同的記憶體位置,如下圖左。

另一方面,ptr 是一個指標,並且只能指向一個長度為 4 的 int 陣列,如上圖右。與一般 int 指標不同的是,ptr + 1 將會指向 4×4 bytes 後的地方,而不是 4 bytes 後的位置(註:通常 int 的長度為 4 bytes)。舉例來說,若 ptr 指向的位址是 0xF000 6000,那 ptr + 1 將會指向 0xF000 6010

#include <iostream>

int main()
{
    int array[4][4] = {{ 0 }};

    int *int_ptr = array[0];
    int (*array_ptr)[4] = array;

    std::cout << "int_ptr     points to " << int_ptr << std::endl;
    std::cout << "int_ptr + 1 points to " << int_ptr + 1 << std::endl << std::endl;

    std::cout << "array_ptr     points to " << array_ptr << std::endl;
    std::cout << "array_ptr + 1 points to " << array_ptr + 1 << std::endl;
    /*
    result:
        int_ptr     points to 0x61fec8
        int_ptr + 1 points to 0x61fecc

        array_ptr     points to 0x61fec8
        array_ptr + 1 points to 0x61fed8
    */

    return 0;
}

Example of Application:array of pointers

array of pointers 可以用來宣告一個可變大小的矩陣,例如我們想建造一個 $4\times n$ 的矩陣,此時我們就可以利用 array of pointers 完成。

void print_matrix(int size) {
    int *matrix[4];
    for (int idx = 0; idx < 4; ++idx) {
        matrix[idx] = new int[size];
        for (int jdx = 0; jdx < size; ++jdx) {
            matrix[idx][jdx] = 0;
            std::cout << matrix[idx][jdx] << " ";
        }
        std::cout << std::endl;
    }
}

但是個人平常比較少使用到 array of pointers,因為 std::vector 或是 dynamic array 都可以完成相同的要求,而且彈性比較高。我認為 array of pointers 主要的優勢應該在於速度快於其它兩者,因此對於速度有極致要求的狀況下可以考慮使用。以下程式碼簡單做一個測試,我們利用 array of pointers 以及 dynamic 的方式分別定義兩個矩陣 matrix1matrix2,並且測試將矩陣內的所有元素值設為 10 所需的時間,藉此我們可以一窺 array of pointers 和 dynamic array 的記憶體存取速度差距。最後我們發現存取 $100\times 100000$ 大小的矩陣,array of pointers 需要的時間都比 dynamic array 還要快 0.001~0.007 秒,大約暫總時間 2%~10%。

/*
Time of static array: 0.048
Time of dynamic array: 0.055
*/
void print_performance() {
    int size = 100000;
    clock_t start, finish;

    int *matrix1[100];
    int **matrix2 = new int*[100];

    for (int idx = 0; idx < 100; ++idx) {
        matrix1[idx] = new int[size];
        matrix2[idx] = new int[size];
        for (int jdx = 0; jdx < size; ++jdx) {
            matrix1[idx][jdx] = 0;
            matrix2[idx][jdx] = 0;
        }
    }

    start = clock();
    for (int idx = 0; idx < 100; ++idx) {
        for (int jdx = 0; jdx < size; ++jdx) {
            matrix1[idx][jdx] = 10;
        }
    }
    finish = clock();
    std::cout << "Time of static array: " << ((double)(finish - start))/CLOCKS_PER_SEC << std::endl;

    start = clock();
    for (int idx = 0; idx < 100; ++idx) {
        for (int jdx = 0; jdx < size; ++jdx) {
            matrix2[idx][jdx] = 10;
        }
    }
    finish = clock();
    std::cout << "Time of dynamic array: " << ((double)(finish - start))/CLOCKS_PER_SEC << std::endl;
}

Example of Application:pointer to array

假設我們正在開發韌體,並且有一連串的記憶體位置 0xF000 0000 - 0xF000 03FF 可以用來設置暫存器的值,而且這些暫存器分為兩組,每組長度 512 bytes,分別用來設置 channel 0 (0xF000 0000 - 0xF000 01FF) 和 channel 1 (0xF000 0200 - 0xF000 03FF) 上的暫存器。此時我們可以定義一個指向長度為 512 陣列的指標,並且使該指標指向 0xF000 0000,我們就可以利用該指標輕鬆對 channel 0 和 channel 1 做設定。

以下範例我們將 R32_REG 定義為一個 pointer to array,且陣列長度為 512。因此 R32_REG[0] 指向的記憶體位置是 0xF000 0000,而 R32_REG[1] 則直接跳到 0xF000 0200,也就是 channel 1 的位置。假設我們要設置第 12 個暫存器,我們可以利用 R32_REG[0][12] 以及 R32_REG[1][12] 找到該暫存器的位置並直接對其做設置。

#define REG_START 0xF0000000
#define R32_REG ((int (*)[512])REG_START)

void print_register() {
    std::cout << "Channel 0 first register: " << R32_REG[0][0] << std::endl;
    std::cout << "Channel 1 first register: " << R32_REG[1][0] << std::endl;
}

其它相關文章

還是搞不懂 virtual function 嗎?來看這篇吧!簡明 C++ virtual function 的機制與概念
深入 virtual function:搞懂 virtual Function 的使用規則