在C/C++中,數組名相當於一個指針,指向數組的首地址。這里“相當於”不代表等於,數組名和指針還是有很多區別的,這個在《C陷阱與缺陷》里有詳盡的講述。而這里要說的是對於數組名取地址的這么一個操作。
如果聲明有如下數組:
int arr[5];
那么,&arr這個操作得到了什么值呢?
如果簡單的認為arr就是一個指向數組首地址的指針的話,那么很自然會想到&arr得到的是一個指向存放arr這個指針的指針,也就是一個二級指針,然而事實卻並不是這樣。
觀察以下代碼:
int arr[5]; printf("%p\n%p\n", arr, &arr); int *pointer = new int[5]; printf("%p\n%p\n", pointer, &pointer);
在我的電腦上運行的結果如下:
0024FC08
0024FC08
0042B158
0024FBFC
我們可以看到,這里pointer這個指針確實如我們所想指向了不同的地址,但是,arr這個數組名的表現卻和指針不同,arr和&arr指向的地址是一樣的。
這里便體現除了數組名和指針的一個不同之處:對數組名進行&操作,並不是取其地址,而是得到了指向整個數組的指針。也就是說,arr與&arr指向的是同一個地址,但是他們的類型不一樣。arr相當於&arr[0],類型是int *,而&arr是指向整個數組的指針,類型是int (*)[5]。
而這樣的區別顯然也會在對指針或者數組名的加減操作中體現出來,看如下代碼:
int arr[5] = { 1, 2, 3, 4, 5 }; printf("%d\n", *arr); printf("%d\n", *(arr + 1)); printf("%d\n", *((int*)(&arr + 1))); printf("%d\n", *((int*)(&arr + 1) - 1));
輸出為:
1 2 -858993460 5
前兩個輸出是很好理解的,對指針的加一操作會使指針按照指針的類型移動相應的位置。
第三行輸出中,首先得到了指向整個數組的指針,對其進行加一操作,指針就指向了整個數組后面的地址,也就是5后面的地址,那么此時將其轉化為int*類型再輸出,就得到了該未初始化過的值。
第四行輸出中,過程為,指針指向5后面的地址,轉為int*類型,此時的減一操作使指針移動一個int類型大小的地址空間,那么便指向了5。
這種特性推廣到二維至多維指針的情況下都是類似的。