1. 程式人生 > >C/C++陣列本質論

C/C++陣列本質論

C/C++陣列本質論
理解陣列名
注:為方便講解,本節所說的指標的型別,指的是指標指向的型別。
1.對陣列名的簡單理解
陣列名錶示的是陣列的首地址。比如一維陣列int a[11]的陣列名a就表示陣列的首地址。這裡的首地址讓人產生誤解,認為陣列名錶示的是整個一維陣列的地址,其實陣列名指向的是陣列中第一個元素地址的指標(即a指向的地址是&a[0]),表示整個陣列的地址是&a,&a表示的是一個包含有4個元素的一維陣列的地址。雖然a和&a的地址值是相同的,但是它們的型別不一樣,a的型別是“指向int的指標”,而&a的型別是“指向一個有11個元素的一維陣列的指標”。
2.對陣列名的理解
關鍵內容,請認真理解。
(1)對陣列名的理解,關鍵是要明白陣列名所表示的指標的型別。
(2)重點規則:陣列名是指向第一個元素地址的指標,這個指標的型別是“指向某型別的指標”,這個指標是個常量。比如int a[4]={0};,陣列名a是一個指向第一個元素a[0]地址的指標(即a指向的地址是&a[0]),它的型別是“指向int的指標”,這個指標是個常量,即不能改變它的值;再比如int b=1;,則a=&b;是錯誤的。
(3)對多維陣列的陣列名的理解:對於多維陣列,重點是要理解它的第一個元素是什麼。通常講的多維陣列其實就是陣列中的陣列,即元素為陣列的陣列。因為陣列名是指向陣列中第一個元素地址的指標,所以多維陣列的陣列名指向的也是第一個元素地址的指標,但這個元素又是一個數組或多維陣列。因此對於N維陣列,陣列名是指向第一個元素地址的指標,這個指標的型別是“指向N-1維陣列的指標”,指向的陣列有N-1維,每維有相對應的X個元素,每個元素都是某型別的(如int)。而一維陣列的陣列名的型別一般是“指向某型別的指標”。
示例:
 int a[3];,陣列名a指向的是第一個元素地址&a[0]的指標,其型別是“指向int的指標”。
 a[2][3];,陣列名a指向的是第一個元素a[0]地址的指標(即a指向的地址是&a[0]),這個元素是一個有3個元素的一維陣列,因此陣列名是一個指向有3個元素的陣列的指標,即陣列名與指標(*p)[3]的型別相同,它的型別是“指向一維陣列的指標”。這個一維陣列有3個元素,即陣列名代表的地址是第一個元素的地址&a[0],這與一維陣列的陣列名是相同的。
 b[3][4][5]={0};,陣列名b是指向第一個元素b[0]地址的指標,即b指向的地址是&b[0]。這個元素儲存的是45的二維陣列,因此這個指標的型別是“指向二維陣列的指標”,指向的陣列有兩維,第一維有4個元素,第二維有5個元素。注意:多維陣列是陣列的陣列,三維陣列b的第一個元素是b[0],而不是b[0][0][0]。
(4)從以上概念可以看出,多維陣列的陣列名所表示指標的型別與多維陣列的第一維的大小是沒有關係的,只與除第一維外的其他維的大小有關係,但這並不意味著第一維的大小就沒有作用了。
(5)對陣列名取址的運算:若對陣列名進行取址運算,則產生的結果是指向整個陣列的指標,即對於一維陣列名,取址後的型別是“指向一維陣列的指標”,這個陣列有X個元素;對N維陣列名取址後的型別是“指向N維陣列的指標”,這個N維陣列的每一維有X個元素。比如int a[2][3][4]={0};,則&a;的型別是“指向三維陣列的指標”,這個陣列的第一維有二個元素,第2維有3個元素,第三維有4個元素。
(6)注意:早期C語言沒有“陣列的地址”這一概念,所以對陣列名取址要麼非法,要麼就等於陣列名本身。
(7)陣列名的型別相同,應滿足以下條件。
① 對於N維陣列,宣告時的元素型別要相同。
② 陣列名的型別應指向相同維數的陣列。
③ 指向的陣列的每一維的元素個數必須相同。
④ 示例:int a[2][3][4]; int b[5][3][4]; int c[2][3][5]; float d[2][3][4]
 陣列名a和b:所表示的指標具有相同的型別,a和b的元素的型別相同,都是int型別。且a和b的型別都是“指向二維陣列的指標”,指向的陣列的第一維都有3個元素,第二維都有4個元素。
 陣列名a和c:所表示的指標具有不相同的型別,雖然它們都是指向二維陣列的指標且元素的型別相同,但陣列名a指向的二維陣列的第二維有4個元素,而c指向的二維陣列的第二維有5個元素,即它們所指向的陣列的每一維的元素個數不相同。
 陣列名a和d:具有不相同的型別,雖然它們都是指向二維陣列的指標,且每一維相對應的元素個數也相等,但a和d的元素型別不相同,a的元素型別是int,而d的元素型別是float。
3.陣列名與指標的關係
(1)對於N維陣列a,其陣列名a與&a[0],&a[0][0],&a[0][0]…[N]的地址值是相同的,也就是說,指向的是相同地址的指標。但要注意,這些指標的型別並不相同,雖然陣列名a和&a[0]型別相同,都是“指向N-1維的指標”,&a[0][0]和a[0]型別相同都是“指向N-2維的指標”,但是&a[0]和&a[0][0]型別並不一樣,因為對指標進行運算時,指標偏移多少個位元組是由其型別決定的,所以在進行運算時它們的偏移量是不一樣的。比如有二維陣列int a[2][3],假設int型別佔據4個位元組的記憶體空間,則&a[0]所指向物件的長度佔據12個位元組的記憶體空間(因為它指向的物件是具有3個元素的陣列),而&a[0][0]所指向物件的長度只佔據4個位元組的記憶體空間,對其進行算術運算時,&a[0]+1將移動12個位元組,而&a[0][0]+1則只移動4個位元組。對於N維陣列也是同樣的道理。
(2)對N維陣列的陣列名做加法運算,將使其指向N維陣列中第X個元素的地址。比如對於N維陣列a,可以使用&a[0]+1或a+1來表示指向第二個元素的地址&a[1]。
(3)對於N維陣列,直接書寫成M(0<M<N)維陣列,當對其進行指標偏移運算時,並不能像陣列名那樣直接進行偏移,而應將其替換為指向的實際地址再進行偏移,否則容易出錯。比如int a[3][4][5][6]={0};,則a[1]指向的是&a[1][0],當進行a[1]+1偏移運算時,並不是指向a[2],而是指向&a[1][1]。也就是說,a[1]+1並沒有指向地址&a[2][0],因為a[1]+1=&a[1][0]+1=&a[1][1](具體請參閱後文中的“指標與陣列的混合運算”)。
4.判斷指標指向的是幾維陣列的指標
(1)對於N維陣列,若直接書寫成M(0<M<N)維陣列,則指標是“指向N-M-1維的指標”。指向的陣列有N-M-1維,每一維有相對應的X個元素,指向的地址是M維之後第一個元素的地址。比如int a[2][3][4][5][6][7]={0};,則a[1][2][3]的指標指向的是二維陣列(6-3-1)的指標,這裡a是六維陣列,即N=6,書寫成三維陣列,即M=3。再比如int a[3][4][5][6]={0};,則a[2];是指向&a[2][0]的指標,它的型別是“指向2維陣列(4-1-1)的指標”,這個2維陣列第一維有5個元素,第二維有6個元素;a[1]指向的是&a[1][0],型別是“指向二維陣列的指標”,這個二維陣列的第一維有5個元素,第二維有6個元素,可以看到a[2]與a[1]的型別是相同的,但指向的地址值不相同。
(2)對於N維陣列,若書寫成帶有&(取址)運算子的M維陣列形式時,則指標指向的是“N-M維的指標”,或者說M維之後還有多少維就是指向多少維的指標。比如int a[2][3][4][5][6][7]={0};,則&a[1][2]指向的是四維陣列(6-2)的指標,這裡a是六維陣列,即N=6,書寫時是帶取址運算子的2維陣列,因此M=2。或者說a是6維陣列,而&a[1][2]只寫了二維,二維之後還有四維,因此&a[1][2]是指向四維陣列的指標。
5.陣列名與指標的不同
(1)當陣列名作為sizeof的運算元時,返回的是整個陣列的長度,而不是指向陣列的指標的長度(在32位機器上指標的長度一般為4個位元組)。比如int a[5]={0};,則sizeof(a);的結果是20(假設int型別佔4個位元組),而不是指標的長度4,因為a有5個元素,每個元素佔4個位元組;再比如int a[3][4]={0};,則sizeof(a);的結果是48,即344=48。
(2)當陣列名作為&的運算元時,所產生的將是一個“指向一維或多維陣列的指標”,而不會返回指標本身的地址。比如int a[4];,則&a;將產生一個“指向一維陣列的指標”,這個一維陣列有4個元素。
(3)注意:陣列名雖然是一個指標,但它與指標並不完全相同,比如陣列名作為sizeof和&的運算元時。
以上內容摘自本人所作《C++語法詳解》一書,電子工業出版社出版