訪問二維數組的指針問題
一級/二級指針訪問二維數組看似是個簡單的問題,但在實際編程中發現還是有不少值得學習的細節問題,本文總結如下
指針解引用過程
聲明一個指針類型使用該指針訪問數據的解引用過程如上圖所示,關鍵點如下
- 要理解指針P本身的值被解釋爲數據的起始內存地址
- 指針P指向的數據類型TYPE是什麼(TYPE當然還是一個指針類型,從而有了後文的二級指針使用注意事項)
- *P返回的是從起始內存地址開始sizeof(TYPE)個字節數據
- 由於指針P指向的數據類型TYPE,所以P++或者P+1不是按一個字節做偏移,而是按sizeof(TYPE)個字節做偏移,這裏是最容易引起錯誤的地方
二維數組的訪問
理解了指針的解引用過程,就能理解二維數組訪問過程中幾個細節的問題了
問題1:指針賦值二維數組
int main(void)
{
float A[K][N]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
float* p1 = A;//編譯出錯
float** p2 = A;//編譯出錯
return 0;
}
上面的代碼在編譯階段就會發生如下錯誤,所以證明二維數組的類型與一級/二級指針不是同一種類型可以直接賦值
error: cannot convert ' float (*)[4] ' to ' float* '
error: cannot convert ' float (*)[4] ' to ' float** '
問題2:指針訪問二維數組
int main(void)
{
float A[K][N]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
float* p1 = (float*)A;
float** p2 = (float**)A;
std::cout << " address of A: " << A << " sizeof(A): " << sizeof(A) << std::endl;
std::cout << " address of p1: " << p1 << " addressof (p1+1): " << (p1+1) << " sizeof(p1): " << sizeof(p1) << " sizeof(*p1): " << sizeof(*p1) << std::endl;
std::cout << " address of p2: " << p2 << " addressof (p2+1): " << (p2+1) << " sizeof(p2): " << sizeof(p2) << " sizeof(*p2): " << sizeof(*p2) << std::endl;
return 0;
}
強制類型轉換可以解決二維數組賦值給一級/二級指針的問題
運行上面的程序獲得的結果如下:
address of A: 0x7ff4c8a368 sizeof(A): 48
address of p1: 0x7ff4c8a368 address of (p1+1): 0x7ff4c8a36c sizeof(p1): 8 sizeof(*p1): 4
address of p2: 0x7ff4c8a368 address of (p2+1): 0x7ff4c8a370 sizeof(p2): 8 sizeof(*p2): 8
上述結果的差異性用下圖解釋:
有了上面的基礎,就可以進一步解釋爲什麼不能用二級指針訪問二維數組了
int main(void)
{
float A[K][N]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
float* p1 = (float*)A;
float** p2 = (float**)A;
std::cout << " p2[0][0]: " << p2[0][0] << std::endl; //發生segmentation fault crash
return 0;
}
運行上面的程序試圖通過二級指針p2[0][0]去訪問二維數組A[0][0]元素,結果程序直接發生segmentation fault crash,原因用下圖解釋
所以通過二級指針去遍歷二維數組是不正確的,但是卻可以通過一級指針去訪問二維數組
int main(void)
{
float A[K][N]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
float* p1 = (float*)A;
float** p2 = (float**)A;
int row = K;
int col = N;
for(int i=0;i<row;i++)
{
std::cout << std::endl;
std::cout << " [ ";
for (int j=0; j<col; j++) {
std::cout << p1[i*col+j] << " ";
}
std::cout << " ] ";
}
std::cout << std::endl;
return 0;
}
程序運行後輸出的結果=
訪問過程如下圖所示
問題3:二維數組傳參
基於對問題1和問題2的分析可以知道,將二維數組作爲參數傳遞的時候,匹配的形參不能使用二級指針,但是可以使用一級指針,下面的表格示例了幾種組合
問題4:指針數組與數組指針
int main(void)
{
float A[K][N]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
float *p1[N];
float (*p2)[N];
std::cout << " address of A: " << A << " sizeof(A): " << sizeof(A) << std::endl;
std::cout << " address of p1: " << p1 << " addressof (p1+1): " << (p1+1) << " sizeof(p1): " << sizeof(p1) << " sizeof(*p1): " << sizeof(*p1) << std::endl;
std::cout << " address of p2: " << p2 << " addressof (p2+1): " << (p2+1) << " sizeof(p2): " << sizeof(p2) << " sizeof(*p2): " << sizeof(*p2) << std::endl;
return 0;
}
運行上面的程序會得到類似如下的結果:
address of A: 0x7ff4c8a368 sizeof(A): 48
address of p1: 0x7ff4c8a348 address of (p1+1): 0x7ff4c8a350 sizeof(p1): 32 sizeof(*p1): 8
address of p2: 0x7ff4c8a360 address of (p2+1): 0x7ff4c8a370 sizeof(p2): 8 sizeof(*p2): 16
此處的float *p1[N] 是個指針數組,它與 float (*p2)[N]數組指針 的差異如下圖所示