徹底搞清C/C++中一維陣列,二維陣列,指標,陣列指標和指標陣列以及指向指標的指標,行地址和列地址之間的關係

發表於2016-11-18

如果配合http://wenku.baidu.com/view/acf838ef856a561252d36fe3.html去看的話下面的內容比較容易理解

從一道題目說起。

一、先解釋 typedef int int_array[4];

關鍵字typedef用來自定義資料型別,這是所有教材都這樣講的,但不要理解為新建立了一個資料型別,而是將已有的一個型別賦予個新名稱而已,即起一個別名。

具體對這個語句來說,別名就是:int_array。而[4]不屬於名字,而表示一種已有的資料型別,即:給一個大小為4的int陣列取一個別名為int_array。

那如何知道是這樣定義的呢?很簡單。

首先,int a[4];這可是常見的定義格式。再在其前面新增關鍵字typedef,變成 typedef int a[4];最後將陣列名a改為自己想要的一個別名int_array即可。注意:原本的a本意是陣列名,屬於變數範疇,而int_array則是新資料型別名(即別名),本質不一樣了哦。祥見譚浩強的那本經典教材。

二、語句 int_array *p =ia; 的含義

首先,它是一個定義語句,即用自定義的資料型別int_array來定義一個該型別的一個指標變數ia。

ia是一個什麼東東呢?它是一個二維陣列名。

對於一維陣列a,我們有:&a[0]等價於a,即都表示該一維陣列首元地址。

那麼,對於二維陣列這個性質還成立嗎?沒錯,同樣成立。即:

&ia[0]等價於ia。

所以,int_array *p =ia;與int_array *p =&ia[0];是等價的。

但是,&a[0]與&ia[0]含義是不一樣的。前者代表了一維陣列中首元地址,而後者則代表二維陣列中第一行的行首地址。

行首地址與行首元地址,它們的值用cout輸出來那肯定是一樣的。但它們與指標的操作扯上關係時,就不一樣了,前者以行為基本單位,後者以一個元素為基本單位,切記。

現在我們應該明白了,語句int_array *p =ia;的作用是定義p後,並初始化p,即用p來指向二維陣列的第一行(整個這一行),即ia[0],不是第一行的首元ia[0][0]哦。當然,對p這樣初始化是正確的,因為p要指向的正是大小為4的一維陣列,而二維陣列ia的每一行正好就是4個元素。
ia[0]可認為是首行的陣列名。ia[1]、ia[2]類推。

三、語句 ++p 的含義

由上可知,p既然首先指向第一行ia[0],那麼(p+1)不就指向第二行ia[1]嗎?正是如此。

於是外迴圈的終止條件就應該是不存在的第四行,即ia[3],所以終止條件就是:p!= ia+3。

四、語句 int *q=*p; 的含義

如上所述,先定義整型指標q,並初始化為*p。

*p是什麼意思?

前面已得到:p 被初始化為ia,即&ia[0]。那麼*p就代表ia[0]。

即:p儲存的是首行地址,於是,*p就直接代表了該行,即整個這一行。

而前面已經明確的講了ia[0]代表的是首行的陣列名,當然它是一維的。而一維陣列名不就代表了這一行的首元地址嗎?於是,就有
q=*p等價於q=ia[0],也等價於q=&ia[0][0]。

再於是,q指向了一個一維陣列的首元。切記,不能說q指向了一個一維陣列。再再於是,++q就表示&ia[0][1]。當然了,再執行一遍++q,就表示&ia[0][2]了。

再解釋一下*p+4。

剛剛講了*p直接代表了某一行,即ia[0]或ia[1]或ia[2],也講了這些ia[0]或ia[1]或ia[2]就代表該行的行陣列名。當然,都是一維的。

回憶一下,一維陣列名加一個數字代表什麼呢?例如a是一個一維陣列名,a+4表示什麼呢?答案是:&a[4],即該一維陣列的第五個元素a[4]的地址。

所以*p+4表示:p所指那一行的第五個元素,當然這對於本題來說是不存在的,所以就做為內迴圈的終止條件咯。

*q代表什麼呢?

q指向的是一個具體的元素,那麼*q就直接代表了該元素的記憶體空間。那麼,cout它就是輸出該元素的值。

為了能更好地理解陣列指標,與普通指標及二級指標的區別,下面舉例說明一下。

例如:{int a[4][5];int (*p)[5]=a;}這裡a是個二維陣列的陣列名,相當於一個二級指標常量;p是一個指標變數,它指向包含5個int元素的一維陣列,此時p的增量以一維陣列長度為單位;p+i是二維陣列a的i行的起始地址,*(p+2)+3表示a陣列2行3列元素地址(第一行為0行,第一列為0列),*(*(p+2)+3)表示a[2][3]的值。

再看一道題

一道面試題引發的問題,首先要知道[]的優先順序高於*,題目:

char **p,a[6][8]; 問p=a是否會導致程式在以後出現問題?為什麼?

直接用程式說明:

編譯,然後就會發現通不過,報錯:錯誤 1 error C2440: “=”: 無法從“char [6][8]”轉換為“char **”

於是乎,我看了下《C專家程式設計》裡10.5節—使用指標向函式傳遞一個多維陣列。

方法一,函式是 void fun(int arr[2][3]); 這種方法只能處理2行3列的int型陣列。

方法二,可以省略第一維的長度。函式是 void fun(int arr[][3]);這種方式雖然限制寬鬆了一些,但是還是隻能處理每行是3個整數長度的陣列或者寫成這種形式 void fun(int (*arr)[3]);這是一個陣列指標或者叫行指標,arr和*先結合使得arr成為一個指標,這個指標指向具有3個int型別資料的陣列。

方法三,建立一個一維陣列,陣列中的元素是指向其他東西的指標,也即二級指標。函式是 int fun(int **arr);這種方法可以動態處理各行各列不一樣長度的資料。

注意:只有把二維陣列改成一個指向向量的指標陣列的前提下才可以這麼做!比如下面的程式可以正常輸出abc:

注意:陣列指標又叫指向一維陣列的指標,資料型別 (*指標變數)[一維陣列維數] eg:int (*p)[4];指向指標的指標(二級指標) eg int **pp;

pp就是行地址,*pp就是列地址;pp+1還是行地址,*pp+1還是列地址。

又如:
///////////////!!!!!!!!!!!!!!!!!!!!!!!!!!!!二維陣列訪問方式如下!!!!!!!////////////////////////////////////////////////////

(1) 方法一

(2)方法二

還可以改改:

與下面的比較一下

結果都是一樣的,但我們最好習慣用char*name[]這樣指標陣列的定義,因為char name[][17]雖然行可以任意,但列卻不能任意。只有指標陣列才能實現任意的行和列。
還比較一下對int 型的二維陣列的輸出:

(3)方法三
用一維指標遍歷二維陣列

看看 對一維陣列的訪問

與下面的比較

也可定義這樣的,只要記住指標陣列中的內容是指標即地址就行

在《C專家程式設計》10.3節的小啟發裡講的很透徹:(以下這段文字及對比一定要認真分析!)

很有用的二維陣列知識,看看二維陣列!!!!!!!!!!!!!!!!!

這五句輸出的結果值是一樣的,都是地址值,但是表示的含義不一樣。

所以我總結的二維陣列一些性質:

a+1表示行地址 而前面加了個*號就表示列地址了 如*(a+1)。

a[1]表示列地址,前面加個&號就表示行地址了,如&a[1].

&a[1][0]表示列地址,注意當* & [] 這三個符號組合出現的次數為偶數次的話就表示真正的內容值,出現奇數次就表示該內容的地址。

但是請注意& 和()不能組合到一起,所以&(a+1)是錯誤的。

行地址前加*就表示列地址了,列地址前加*號就表示取內容值,而列地址前加&表示行地址了。所以只有行地址前加*號才能變列地址,加&報錯,而列地址前加&變行地址,

加*就取得值了,所以對二維陣列來說只有列地址前加*號,才能得到內容。如果是行地址,這先要轉化為列地址,才能得內容。而行地址套上[]符號就變成列地址瞭如

a->行地址 a[0]->列地址.總之一點只有當成為列地址後再在前面加*才能取到真正的內容.

看看如下內容:

對這個的改進,不要輸出最後的亂碼

就不會輸出最後的亂碼了,哈哈哈哈!!!!!!!!!!!!!!!!!!!!!!

再看看輸出int的二維陣列

陣列和指標引數是如何被編譯器修改的?

“陣列名被改寫成一個指標引數”規則並不是遞迴定義的。陣列的陣列會被改寫成“陣列的指標”,而不是“指標的指標”:

實參 所匹配的形參

陣列的陣列 char c[8][10]; char (*)[10]; 陣列指標

指標陣列 char *c[10]; char **c; 指標的指標

陣列指標(行指標) char (*c)[10]; char (*c)[10]; 不改變

指標的指標 char **c; char **c; 不改變

下面再看一個網友的一段分析相當給力的程式碼:

再看一個有意思的,看看對指標的掌握情況,“行地址”和“列地址”的區別

記住 一維陣列的陣列名是列地址,二維陣列名是行地址,雖然陣列名都是地址,但本質不一樣,行地址始終要轉化為列地址,才能得到內容值
轉化規則:行->列:在行前加*號,列->轉化->行,在列前加&。

再看下面

五、總結

陣列指標和二維陣列有關,你可以到定義了陣列指標後將二維陣列名賦值給他;指標陣列和指向指標的指標有關,你可以將兩者賦值。

相關文章