這一章我們來討論一下陣列的內涵,對陣列的內部構造進行一次解剖,看看裡面究竟隱藏了什麼秘密。 有了前面兩章對陣列名和c語言陣列本質的澄清,再來理解這一章的內容,就容易多了。
在下面的敘述中,筆者會用到乙個運算子sizeof,由於在不同的編譯器和編譯模式下,對乙個位址進行sizeof運算的結果有可能是不同的,為了方便討論,我都假設位址長度為4個位元組。
多數教材在講述陣列的時候,都是把重點放在外部表現形式上,很少涉及陣列的內部,只嫠唚閎綰巫觶?卻忽視了為什麼要這樣做。在解釋的過程中,還會列出各種各樣的表示式,例如:a、a+1、a[0]、a[0][0]、&a[0]、&a[0][0]、*(a+1)等等,讓人眼花繚亂。但實際上真正能夠用來描述陣列內部構造的表示式只有其中的幾個。
上一章講到,c語言的陣列實現並非真正的多維陣列,而是陣列巢狀,訪問某個元素的時候,需要逐層向下解析。仍然以上一章的例子陣列int a[7][8][9]來說,第一維元素0的值a[0]是a[0]所代表的那個陣列的首位址,這個表示式在c語言的陣列裡面具有特殊的意義,之所以特殊,不僅僅在於它所代表的東西與一般的位址不同,而且型別也並非一般的位址型別,它的型別叫做陣列型別,陣列型別這個名稱在絕大多數教材中是從來沒有出現過的,在c89標準中,也僅僅出現在介紹陣列定義的那一段。具有陣列型別的位址跟一般型別位址的主要區別,在於長度不一樣,對乙個一般型別的位址進行sizeof運算,結果是4個位元組,而a[0]由於代表了乙個陣列,sizeof(a[0])的結果是整個陣列的長度8x9xsizeof(int),並非4個位元組。具有陣列型別的位址跟陣列名一樣都是乙個符號位址常量,因此它必定是乙個右值。陣列型別在陣列的定義與引用中具有非常重要的作用,它可以用來識別乙個識別符號或表示式是否真正的陣列,乙個真正陣列的陣列名,是乙個具有陣列型別的符號位址常量,它的長度,是整個陣列的長度,並非一般位址的長度,如果乙個識別符號不具備陣列型別,那它就不是乙個真正的陣列。在後面的章節裡,還會再次使用這個概念。
與a[0]類似的陣列型別位址還有a[0][0],a[0][0]是a[0]的下一層陣列,因此sizeof(a[0][0])的結果是9xsizeof(int)。類似地,對於乙個三維陣列:
a[i][j][k]
a、a[x]、a[x][y](其中x、y大於等於0而小於i、j)都是具有陣列型別的位址常量,而且都是乙個右值。這一點要牢牢記住。正是由這些特殊型別的位址構成了整個陣列。
以上結論對於n維陣列同樣適用。
接下來跟各位一起討論一下跟陣列有關的各種表示式的意義及其型別:
&a[0][0][0]:
&a[0][0][0]僅僅是乙個位址,它的意義,僅僅表示元素a[0][0][0]的位址,sizeof(&a[0][0][0])的結果是4。不少人把它說成是陣列a的首位址,這是錯誤的,這是對陣列首位址概念的濫用。真正能代表陣列a的陣列首位址只有a本身,a與&a[0][0][0]的意義根本就是兩回事,真正的陣列首位址是具有陣列型別的位址,sizeof(a)結果是ixjxkxsizeof(int),而不是4,只不過由於a[0][0][0]位置特殊,恰好是陣列a的第乙個元素,所以它們的位址值才相同。而對於a[0]和a[0][0],它們是在陣列a內部a[0]和a[0][0]所代表的那個陣列的首位址,它們的位址值也是由於位置「特殊」,因此才跟a和&a[0][0][0]一樣。這一點一定要區分清楚了。
a+i:
a[i]+j:
跟上面的類似,a[i]+j是a[i]所代表的那個陣列的元素j的位址,sizeof(a[i]+j)的結果也為4。
&a:對陣列名取位址在c標準裡面是未定義的。這個表示式曾經引起過爭論,焦點在於對乙個右值取位址的合法性。c89規定&運算子的運算元必須具有具體的記憶體空間,換言之就是乙個左值,但陣列名卻是乙個右值,按照&運算子的要求,這是非法行為。因此,早期的編譯器通常規定&a是非法的。但不知道什麼原因,現在的編譯器都把&a人為地定義成乙個比a高一級而位址值跟a一樣的位址,但作為比a高一級的位址,有乙個行為卻非常怪誕,sizeof(&a)的結果跟sizeof(a)相同,這也是人為的痕跡。筆者傾向於把&a定義為非法,應該維護&運算子的權威性,而不是在規定對某個右值取位址為非法的同時,又允許對另乙個右值取位址,這是互相矛盾的。
&a[i]和&a[i][j]:
跟&a一樣,也是未定義的,同樣不符合&運算子的規則。由於a[i]是a[i][j]的上一層陣列,有些人可能會想當然地以為:a[i]=&a[i][j],錯也,實際上,由於a[i][j]=*(a[i]+j),因此&a[i][j]=&*(a[i]+j),結果是a[i]+j。對於sizeof(&a[i])和sizeof(&a[i][j]),由於是未定義的,因此有些編譯器規定其值跟sizeof(a[i])和sizeof(a[i][j])相同,有些編譯器卻規定為4,就是乙個位址的長度。
第三章 陣列的解剖學
這一章我們來討論一下陣列的內涵,對陣列的內部構造進行一次解剖,看看裡面究竟隱藏了什麼秘密。有了前面兩章對陣列名和c語言陣列本質的澄清,再來理解這一章的內容,就容易多了。在下面的敘述中,筆者會用到乙個運算子sizeof,由於在不同的編譯器和編譯模式下,對乙個位址進行sizeof運算的結果有可能是不同的...
陣列與指標的藝術 第三章 陣列的解剖學
注意 本系列文章 csdn部落格 http blog.csdn.net supermegaboy archive 2009 11 23 4855027.aspx 感謝飛天御女豬大牛!c c 的陣列不同於vb等語言的陣列,是有層次的,這個層次指的不是維度,而是象俄羅斯有名的套娃一樣,一維套一維,亦即陣...
第三章 Data語意學
1 關於data member的繫結 對於memner function的本體分析,會直到整個class的宣告都出現了才才開始。因此乙個inline member function軀體內的乙個data member的繫結操作,會在整個class宣告之後才發生。但是,對於member function...