這篇文章簡略紀錄一下 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 的方式分別定義兩個矩陣 matrix1
和 matrix2
,並且測試將矩陣內的所有元素值設為 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 的使用規則