二維數組名作形參

之前幫同學調一個程序的時候遇到的,把二維數據改爲全局變量,不通過參數傳遞就沒問題了,否則程序崩潰。

細究一下,二維數據名用於形參時需要注意哪些方面。

測試程序如下:

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

void print1(int **a, int m, int n);
void print2(int (*a)[2], int m, int n);

int main()
{
  int a[2][2]={1,2,3,4};
  print1((int**)a,2,2);
  print2(a,2,2);
  system("pause");
  return 0;
}

void print1(int **a, int m, int n)
{
  for(int i=0;i<m;++i)
    for(int j=0;j<n;++j)
    {    
      printf("%d\n",*(a+i*n+j));
      //printf("%d\n",a[i][j]); 
    }
}

void print2(int (*a)[2], int m, int n)
{
  for(int i=0;i<m;++i)
    for(int j=0;j<n;++j)
    {
      printf("%d\n",a[i][j]);        
    }     
}
程序可以正常運行,而且沒問題。有問題的是print1中註釋掉的語句。可能很多人會覺得二維數組的數組名不就是一個二維指針嗎,爲什麼用a[i][j]的方式訪問不行?註釋掉的語句在執行時,程序崩潰。

原因是這樣的,二維數組的數組名其實是一個int (*)[N]的指針,和 int** 指針還是不一樣的。直觀來看,對前者一次解引用的話,得到的是數組;對後者一次解引用,得到的是指針。其實在調用的時候就可以發現,如果你不進行強制類型轉換,而直接進行這樣的調用 print1(a,2,2); 會提示如下錯誤:cannot convert `int (*)[2]' to `int**' for argument `1' to `void print1(int**, int, int)' 。編譯器已經告訴你,類型不匹配了;這個時候你應該想到的是用第二種方式來定義函數。而有的人可能覺得既然類型不匹配,就給直接強制類型轉換了,結果編譯通過了,但程序崩潰了,更找不出問題。

產生這個問題的根本原因是,對數組名和指針的細微差別沒有認識到。

數組名,在訪問元素時,是直接取址的過程。比如a[i],它是直接在a的地址上進行一個 i (乘以a[i]的類型大小)的偏移。(每個符號的地址在編譯時可知

指針,訪問元素時,是一個間接尋址的過程。比如對int *a; 在a[i]取元素時,首先它會在變量a的地址上取數,取得的值作爲地址再去尋址,然後再進行一個 i (乘以類型大小)的偏移。

這樣的話,對上面測試代碼而言,在main函數中,你聲明的a是數組名,即int (*)[2]類型,傳遞到print2中,在print2函數中用a[i][j]訪問時,是先對形參a進行一個間接尋址,然後直接尋址。而傳遞到print1中,首先你需要強制轉換成int ** 纔可以,在print1中用a[i][j]訪問時,是對形參a進行間接尋址後,再間接尋址,這樣就發生了錯誤。而在print1中用*(a+i*n+j)的方式則是可以的。

這個和下面這類錯誤很類似。就是在一個文件中定義了int a[100]; 然後你想在另一文件中使用它,你用了外部聲明,但是你聲明成了這樣: extern int * a; 這樣,在訪問a數組的元素時,本來應該是按照數組名的直接尋址方式,在這個文件中卻會用指針的間接尋址,就會出現不可預知的錯誤。

也有參考這裏

關於指針和數組名在什麼情況下不同,以後有時間再總結下。

發佈了145 篇原創文章 · 獲贊 12 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章