常常在BBS上看到有人問指針和數組的問題。我曾經也很迷惑,現在,我談談我對指針和數組的理解。歡迎批評指正討論。
1:定義數組
int a[5];
上一句定義了一個數組,名字叫a,它有5個元素,每個元素是int類型。換句話說,a是一個int [5]型的數組。int [5]表示的是類型,只不過是個複合類型,本質上,和int, float,double沒有區別,都是類型。
2:數組的操作。
2.1:sizeof操作。
像1中的數組a,sizeof(a)的值也就是sizeof(int [5])的值,也就是5*sizeof(int)的值。
2.2:加、減、賦值。
當一個數組參與加、減和賦值運算的時候,它首先被轉換(converted to)成指向首元素的指針。
a+0是什麼?這裏數組a要做加法,所以,它首先被轉換成指向首元素的指針。然後再加0。
同理,a-0的操作過程是:a首先轉換成指向首元素的指針,再減0。
int *p = a;合法嗎?合法。數組a在賦值運算的時候,首先a轉換成指向首元素的指針,然後再賦值給p。
a+0,a-0和a,這三者有什麼區別?a+0的類型是int *,a-0的類型是int *,a的類型是int [5],也就是說類型有同。但它們的值是相同的,都是數組a首元素的地址。
2.3 下標運算符[]
大家都知道,要訪問數組a的首元素,用a[0]就可以了。訪問第二個元素,用a[1]就可以了。
那麼,[]究竟是什麼?表達式 E1[E2] 等同於表達式 *((E1)+(E2)),等同於*((E2)+(E1)),等同於E2[E1].
所以a[0]等同於*((a)+(0))即*(a+0)。a+0是指向首元素的指針,那麼*(a+0)就是首元素了。同理 a+1是指向第二個元素的指針,*(a+1)也就是第二個元素了。
2.4 取地址運算符&。
&a是什麼?很明顯,&a是指向a的指針,a的類型是int [5],&a的類型就是int (*)[5]。&a的數值是數組a的首地址。
3:所謂的多維數組。
3.1 二維數組。
首先要明確,C語言中沒有真正的多維數組。C語言中的多維數組本質上是一維數組。
3.1.1 定義
int b[3][5];
看上面的數組b的定義,b是一個一維數組,有3個元素,每個元素都像數組a一樣,也就是說,b的每個元素是int [5]。
既然所謂的二維數組還是一維數組,那麼我們所講述的一組數組的東西都可以用到所謂的二維數組上。
b是什麼?b是int [3][5]類型的變量。只不過這個變量不可被改變。
3.1.2 加、減、賦值。
b+0是什麼?b要做加法了,所以它首先被轉換爲指向首元素的指針,然後再加0。b的元素的類型是int [5],所以b+0的類型就是int (*)[5]。
b-0呢?同上。
int (*p1)[5] = b;是什麼意思?p1是一個指針,指向int [5]。把b賦值給它時候,b首先轉換成指向b的首元素的指針,再賦值。
*(b+0)的類型是什麼?b+0的類型是int (*)[5],那麼*(b+0)的類型就是int [5]。*(b+0)也就是數組b的第一個元素。*(b+1) 也就是數組b的第二個元素。
*(b+0)+0的類型是什麼?*(b+0)的類型就是int [5],那麼*(b+0)+0也就是指向這個整形數組的首元素的指針。也就是說*(b+0)+0的類型是int *型。
*(*(b+0)+0)的類型是什麼?*(b+0)+0是int *, *(*(b+0)+0)就是int。
3.1.3 下標運算符[]
E1[E2]不是和*((E1)+(E2))完全等同嗎,那麼*(*(b+0)+0)也就是*(b[0]+0)也就是b[0][0]。
3.1.4取地址運算符&
&b是什麼?很明顯,&b是指向b的指針,b的類型是int [3][5] ,&b的類型就是int (*)[3][5]。&b的數值是數組b的首地址。
3.2 三維數組
int c[2][3][5]。c是個一維數組,有2個元素,每個元素都像b那樣,也就是說,每個元素都是個int [3][5]。
c+0是指針,類型是int (*)[3][5],指向c的第一個元素。
*(c+0)是第一個元素,也就是第一個int [3][5]。
*(c+0)+0是什麼?*(c+0)是int [3][5],*(c+0)+0就是指向*(c+0)中第一個int[5]的指針。
*(*(c+0)+0)是什麼?是個int [5]。
*(*(c+0)+0)+0是什麼?是指向上面的int [5]的第一個元素的指針。它的類型是int *。
*(*(*(c+0)+0)+0)是什麼?是第一個int。
4:把數組傳給一個函數。
把數組賦值給一個函數的參數,我們已經知道,賦值的時候數組首先轉換成指向首元素的指針。
數組a可以傳給 void f1(int *arr)
數組b可以傳給 void f2(int (*arr)[5])
數組c可以傳給 void f3(int (*arr)[3][5])
在上面三個函數裏,參數arr都是指針了,不再是數組了。所以,數組傳給函數的時候,會丟失維度的信息,所以,很多時候,把一個數組傳給函數時,還要用另一個參數指明數組是幾維的
PS:
數組名是指針?錯。
數組名在+,-,賦值運算時被轉換爲指向數組首元素的指針?對。
二維數組是二級指針?大錯特錯。數組就是數組。所謂的二維數組就是一維數組的數組。
*(a+0)與a[0]完全一樣?對。不信就去查C99標準中的[]運算符。
*(0+a)與0[a]完全一樣?對。不信去查標準。但寫成這樣蛋疼。
a+0和a有什麼區別?區別大了。a是數組,a+0運算時,首先a被轉化成指向首元素的指針,然後再做指針加法。也就是說,a+0和a的數值一樣,類型不一樣。
int *p=a;賦值運算時,a也被轉化爲指向首元素的指針?對。
當數組a做爲參數傳給函數void foo(int a[])也被轉化爲指向首元素的指針?對。
把b傳給函數,參數怎麼寫?void foo(int (*b)[5]),一個指向一組數組的指針。
&a的類型是什麼?是int (*)[5]也就是指向一維數組的指針。但它的值還是a的首地址。
如果你不太清楚int (*)[3][5]和int *[3][5]的區別,請google “右左原則”或訪問此網頁,然後在下載下來的東西里找“右左原則”四個字,讀過之後,對於聲明定義,都看得懂了。
本文來源:http://wildpointer.net/2010/06/10/c_pointer_array/