數組名和指針那點事
今天在查數組名的意義的時候,聯系到指針的知識,對指針有了一種新的理解。
數組是一種特殊的變量,數組名具有雙重含義(初始):
指向數組第一個成員的指針(如果是多維數組則數組成員也為數組);
代表數組本身(其實說成"指向整個數組的指針"更合適)。
一直對第二個含義不太理解,因為感覺上比較抽象,不知道有什麼實際意義。
今天看到一個例子,終於開悟。
int ary[3];
printf("ary = %p\n" "ary + 1 = %p\n", ary, ary + 1);
printf("&ary = %p\n" "&ary + 1 = %p\n", &ary, &ary + 1);
輸出結果:
ary = 0xffb38dc8
ary + 1 = 0xffb38dcc
&ary = 0xffb38dc8
&ary + 1 = 0xffb38dd4
這就比較有意思了,很明顯,ary和&ary都指向了數組的首地址; ary+1相對ary增加了4,一個int型的長度,指向數組第二個成員首地址; 而&ary+1相對&ary則增加了3*4=12,正好是整個數組的大小。
因此數組名稱的第二個含義是只有當數組名前有&時才體現(可能還有其他情況),單獨使用時則相當於一個指向數組成員類型的指針(初始指向第一個成員首地址,另注意數組成員也可以是數組)。
所以對於一個一維數組int ary[3],它的數組名的含義有兩層:
1):
int pAry = ary; //pAry复制了ary的第一层含义
// OR:
// int pAry = &ary[0];
2):
typedef int ARY[3];
ARY *pAry = ary; //pAry复制了ary的第二层含义
以前有一個疑惑。對於二維數組ary2[3][3],ary2,ary2[0]和&ary2[0][0]的值是一樣的,都是整個數組的首地址。但*ary2的值竟然也是一樣的,這是為什麼呢?
先用之前的方法調試看看:
int ary2[3][3];
printf("ary2 = %p\n" "ary2 + 1 = %p\n", ary2, ary2 + 1);
printf("*ary2 = %p\n" "*ary2 + 1 = %p\n", *ary2, *ary2 + 1);
輸出結果:
ary2 = 0xff9fa4a0
ary2 + 1 = 0xff9fa4ac
*ary2 = 0xff9fa4a0
*ary2 + 1 = 0xff9fa4a4
首先ary毫不意外地體現了數組名的第一層含義: 指向數組第一個成員的指針。由於是二維數組,所以數組的第一個成員也是一個數組,它擁有3個整型成員,大小是12字節。所以ary2+1相對ary2增加12。
那麼*ary2中ary2到底體現的哪一層含義呢?*ary2的實際意義又是什麼?
明顯,ary2+1相對ary增加了4,一個整型的大小,所以ary2是相當於ary2[0]的第一層含義的,即指向ary2[0]的第一個成員ary[0][0]。反推回去,如果ary2中ary2是第二層含義(指向整個數組的指針)的話,那ary2應該得到的是整個數組的數據,而不是一個指針值。所以ary2中ary2是體現的第一層含義,即指向數組第一個成員(ary2[0])的指針。
但是這樣解釋新的問題又來了,運算符不是取出指針指向的值嗎?那ary2[0]取出的應該是什麼呢?應該是ary2[0]這個數組!但我們得到的結果呢?是一個int*型的指針!
這個問題折磨了我很久。它看起來並不重要,因為這不影響我們的實際操作,只要知道結論就夠了。但強迫症還是讓我被迫想出一個說服自己的說法:
問題的關鍵還在於開篇對數組名的解讀。數組的名稱可以當成數組本身的指針,也可以當成數組的第一個成員的指針。對於一維數組,這樣的描述看上去沒有問題,因為ary是可以取出數組第一個成員的值的,數組名像指針一樣。但到了二維數組,情況就不一樣了。直接取二維數組名ary2的值取出來的是ary2[0][0]的地址值,對數組名進行運算,*ary2取出來的還是ary2[0[0]]的地址值。
所以認為,編譯器並沒有為數組名在內存中分配空間,而是由編譯器自己做了一個簡單的映射,將數組名的值設為其第一個類型為數組的定義類型的成員的地址(比如int ary[][]的定義類型就是int),然後由編譯器對不同的操作請求對數組名做出不同的解釋。比如,對於三維數組int ary[2][2][2],編譯器把標識符ary[i][i]的值設為ary[i][i][0]的地址值,而對於標識符ary[1],由於ary[1]的第一個成員ary[1][0]不是int型,所以繼續拆分,直到找到第一個int型的成員ary[1][0][0],將ary[1][0]的值置為ary[1][0][0]的地址值。
由此類推,ary, ary[0], ary[0][0]它們的值是一樣的!都是ary[0][0][0]的地址值!不同的只是它們各自管理的內存空間範圍不同而已,並且它們管理的空間起點是相同的,都是ary[0][0][0]的首地址。注意,數組名的值都是直接被編譯器記錄的,而不是寫入內存,所以是不能對數組名賦值的,對數組名的操作其實是對其具有內存空間的成員的操作。這點和指針不一樣,因為指針本身其實是一個變量,它自己本身是有內存空間的,所以多級指針和多維數組變量名有本質區別!指針是一級一級的依次取值,才能得到最裡層的數據,而數組是名依靠映射關系所以直接取得數據。