【程序分析】指針與二維數組的訪問

訪問二維數組的指針問題

一級/二級指針訪問二維數組看似是個簡單的問題,但在實際編程中發現還是有不少值得學習的細節問題,本文總結如下

指針解引用過程

指針解引用過程

聲明一個指針類型使用該指針訪問數據的解引用過程如上圖所示,關鍵點如下

  • 要理解指針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,原因用下圖解釋

二級指針訪問二維數組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;
}

 程序運行後輸出的結果=\begin{bmatrix} 1& 2& 3& 4\\ 5& 6& 7& 8\\ 9& 10& 11& 12 \end{bmatrix}

訪問過程如下圖所示

一級指針訪問二維數組的過程分析

 

問題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]數組指針 的差異如下圖所示

指針數組與數組指針的差異

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章