c語言中陣列和指標是一種很特別的關係,首先本質上肯定是不同的,本文各個角度論述陣列和指標。
一、陣列與指標的關係
陣列和指標是兩種不同的型別,陣列具有確定數量的元素,而指標只是乙個標量值。陣列可以在某些情況下轉換為指標,當陣列名在表示式中使用時,編譯器會把陣列名轉換為乙個指標常量,是陣列中的第乙個元素的位址,型別就是陣列元素的位址型別,如:
int a[5]=;
陣列名a若出現在表示式中,如int *p=a;那麼它就轉換為第乙個元素的位址,等價於int *p=&a[0];
再來乙個:
int aa[2][5]=;
陣列名aa若出現在表示式中,如int (*p)[5]=aa;那麼它就轉換為第乙個元素的位址,等價於int (*p)[5]=&aa[0];
但是int (*p)[5]=aa[0]; 這個就不對了,根據規則我們推一下就很明了了,aa[0]的型別是int [5],是乙個元素數量為5的整型陣列,就算轉化,那麼轉化成的是陣列(int [5])中第乙個元素的位址&aa[0][1],型別是 int *。
只有在兩種場合下,陣列名並不用指標常量來表示--就是當陣列名作為sizeof操作符或單目操作符&的運算元時,sizeof返回整個陣列的長度,使用的是它的型別資訊,而不是位址資訊,不是指向陣列的指標的長度。取乙個陣列名的位址所產生的是乙個指向陣列的指標,而不是指向某個指標常量值的指標。
如對陣列a,&a表示的是指向陣列a的指標,型別是int (*)a,所以int *p=&a;是不對的;
陣列的sizeof問題會在下面中仔細討論。
二、陣列與指標的下標引用
int a[5]=;
如a[3],用下標來訪問陣列a中的第三個元素,那麼下標的本質是什麼?本質就是這樣的乙個表示式:*(a+3),當然表示式中必須含有有效的陣列名或指標變數。
其實a[3]和3[a]是等價的,因為他們被翻譯成相同的表示式(頂多順序不同而已),都是訪問的陣列a中的元素3。
指標當然也能用下標的形式了,如:int *p=a; 那麼p[3]就是*(p+3);等同於3[p](不要**。。。3p,3p),同樣訪問陣列a中的元素3。
根據這一規則,我們還能寫出更奇怪的表示式,如:
int aa[2][5]=;
1[aa][2],這個看起來很彆扭,首先 1[aa],就是*(1+aa),那麼1[aa][2]就是*(*(1+aa)+2),也就是aa[1][2]。
1[2][aa],這個就不對了,因為前半部分1[2]是不符合要求的。
當然在實際中使用這樣的表示式是沒有意義的,除非就是不想讓人很容易的看懂你的**。
三、陣列與指標的定義和宣告
陣列和指標的定義與宣告必須保持一致,不能乙個地方定義的是陣列,然後再另乙個地方宣告為指標。
首先我們解釋一下陣列名的下標引用和指標的下標應用,它們是不完全相同的,從訪問的方式來講。
int a[5]=;
int *p=a;
對於a[3]和p[3]都會解析成*(a+3)和*(p+3),但是實質是不一樣的。
首先對於a[3],也就是*(a+3):
(1)把陣列名a代表的陣列首位址和3相加,得到要訪問資料的位址;
(2)訪問這個位址,取出資料。
對於p[3],也就是*(p+3):
(1)從p代表的位址單元裡取出內容,也就是陣列首位址;
(2)把取出的陣列首位址和3相加,得到要訪問的資料的位址;
(3)訪問這個位址,取出資料。
下面給出乙個例子來說明若定義和宣告不一致帶來的問題:
設test1.cpp中有如下定義:
char s="abcdefg";
test2.cpp中有如下宣告:
extern char *s;
顯然編譯是沒有問題的。
那麼在test2.cpp中引用s結果怎樣呢?如s[3],是『d』嗎?好像是吧
下面我們對test2.cpp中的s[3]進行分析:
s的位址當然是由test1.cpp中的定義決定了,因為在定義時才分配記憶體空間的;
我們根據上面給出的指標下標引用的步驟進行計算
(1)從s代表的位址單元的內容(4個位元組),這裡實際上是陣列s中的前4個元素,這個值是「abcd」,也就是16進製制64636261h,到這一步應該就能看出來問題了;
(2)然後把取出的首位址和3相加,得到要訪問的資料的位址64636261h+3,這個位址是未分配未定義的;
(3)取位址64636261h+3的內容,這個位址單元是未定義的,訪問就會出錯。
下面給出分析的**(可只需觀察有注釋的部分):
view plain
test2.cpp // 執行錯誤
view plain
若test2.cpp中這樣宣告:
extern char s;
這樣就正確了,因為宣告和定義一致,訪問就沒問題了。
所以千萬不要簡單的認為陣列名與指標是一樣的,否則會吃大虧,陣列的定義和宣告千萬要保持一致性。
四、陣列和指標的sizeof問題
陣列的sizeof就是陣列的元素個數*元素大小,而指標的sizeof全都是一樣,都是位址型別,32位機器是4個位元組。
下面給出一些例子:
測試程式:
view plain
vs2010在32位windows7下的執行結果(vc6.0不符合標準):
19232432
4444
43244
1924
下面對程式做逐一簡單的解釋:
(1) sizeof(a); a的定義為int a[6][8],型別是int [6][8],即元素個數為6*8的二維int型陣列,它的大小就是6*8*sizeof(int),這裡是192;
(2) sizeof(*a); *a這個表示式中陣列名a被轉換為指標,即陣列第乙個元素a[0]的位址,'*'得到這個位址所指的物件,也就是a[0],總的來說*a等價於*(&a[0]),a[0]的型別int [8],即大小為8的一維int型陣列,它的大小就是8*sizeof(int),這裡是32;
(3) sizeof(&a); '&'取a的位址,型別是int (*)[6][8],位址型別,這裡大小是4;
(4) sizeof(a[0]); a[0]的型別int [8],即大小為8的一維int型陣列,它的大小就是8*sizeof(int),這裡是32;
(5) sizeof(*a[0]); *a[0]這個表示式中陣列名a[0]被轉換為指標,即陣列的第乙個元素a[0][0]的位址,'*'得到這個位址所指的元素,也就是a[0][0],總的來說*a[0]等價於*(&a[0][0]),a[0][0]的型別是int,它的大小就是sizeof(int),這裡是4;
(6) sizeof(&a[0]); '&'取a[0]的位址,型別是int (*)[8],位址型別,這裡大小是4;
(7) sizeof(a[0][0]); a[0][0]的型別是int,它的大小就是sizeof(int),這裡是4;
(8) sizeof(&a[0][0]); '&'取a[0][0]的位址,型別是int *,位址型別,這裡大小是4;
(9) sizeof(p); p的型別是int *,指向乙個int型元素,位址型別,這裡大小是4;
(10)sizeof(*p); *p取得p所指的元素,型別是int,大小為sizeof(int),這裡是4;
(11)sizeof(&p); '&'取p的位址,型別是int **,位址型別,這裡大小是4;
(12)sizeof(pp); pp的型別是int (*)[6][8],指向乙個大小為6*8的二維int型陣列,位址型別,這裡大小為4,
(13)sizeof(*pp); *pp取得pp所指的物件,型別是int [6][8],即元素個數為6*8的二維int型陣列,它的大小就是6*8*sizeof(int),這裡是192;
(14)sizeof(&pp); '&'取pp的位址,型別是int (**)[6][8],位址型別,這裡大小是4;
五、陣列作為函式引數
當陣列作為函式引數傳入時,陣列退化為指標,型別是第乙個元素的位址型別。「陣列名被改寫成乙個指標引數」,這個規則並不是遞迴定義的。陣列的陣列會被改寫為「陣列的指標」,而不是「指標的指標」。
下面給出幾個例子:
fun1(char s[10])
fun2(char s[10])
fun3(char *s[15])
fun4(char(*s)[20])
以上可以簡單的歸納為陣列作為引數被改寫為指向陣列的第乙個元素(這裡的元素可以是陣列)的指標。陣列作為引數必須提供除了最左邊一維以外的所有維長度。我們還要注意char s[10]和char ** s作為函式引數是不一樣的,因為函式內部指標的型別不一樣的,尤其在進行指標加減運算以及sizeof運算時。
總結:
總結了這麼多,應該對陣列和指標有個較深入的理解了。這些問題的歸根原因還是來自於指標問題,這也正是c語言的精華所在,不掌握這些根本不算掌握c語言,不過掌握了這些也不敢說就等於掌握了c語言:)
深入理解C C 指標
c語言所有複雜的指標宣告,都是由各種宣告巢狀構成的。如何解讀複雜指標宣告呢?右左法則是乙個既著名又常用的方法。不過,右左法則其實並不是c標 準裡面的內容,它是從c標準的宣告規定中歸納出來的方法。c標準的宣告規則,是用來解決如何建立宣告的,而右左法則是用來解決如何辯識乙個宣告的,兩者可 以說是相反的。...
深入理解C C 函式指標
函式指標陣列的妙用 筆者在開發某軟體過程中遇到這樣乙個問題,前級模組傳給我二進位制資料,輸入引數為 char buffer和 int length,buffer是資料的首位址,length表示這批資料的長度。資料的特點是 長度不定,型別不定,由第乙個位元組 buffer 0 標識該資料的型別,共有2...
深入理解C C 函式指標
函式指標陣列的妙用 筆者在開發某軟體過程中遇到這樣乙個問題,前級模組傳給我二進位制資料,輸入引數為 char buffer和 int length,buffer是資料的首位址,length表示這批資料的長度。資料的特點是 長度不定,型別不定,由第乙個位元組 buffer 0 標識該資料的型別,共有2...