前面已經討論了指針和數組的一些區別,然而在某些情況下,指針和數組是等同的,下面討論一下什麼時候指針和數組是相同的。
C語言標准對此作了說明:
規則1:表達式中的數組名被編譯器當做一個指向該數組第一個元素的指針;
注:下面幾種情況例外
1)數組名作為sizeof的操作數
2)使用&取數組的地址
3)數組是一個字符串常量初始值
規則2:下標總是與指針的偏移量相同;
規則3:在函數參數的聲明中,數組名被編譯器當做指向該數組第一個元素的指針。
規則1和規則2結合在一起理解,就是對數組下標的引用總是可以寫成“一個指向數組的起始地址的指針加上偏移量”。如a[i]總是被編譯器解析為*(a+i)的形式。
規則1:表達式中的數組名總被編譯器解析為指針,因此如下語句int a[3];int *p=a;是可以正確編譯執行的。在表達式中a被解析為指向數組第一個元素的指針,那麼賦值符號兩邊的類型匹配,因此可以正確編譯執行。
規則2:下標總是和指針的偏移量相同。C語言中將數組的下標改寫成指針偏移量的主要原因在於指針和偏移量是底層硬件所使用的基本類型。如a[i]中的i總被編譯器解析為偏移量,所以a[i]總是被改寫成*(a+i)的形式,a是指向數組第一個元素的指針,加上偏移量i,表示該指針向後移i個步長,然後取a+i所在單元的內容。由此就可以解釋為什麼C語言中數組的下標可以為負,而且在我看來,C語言中不檢查數組的下標是否越界同樣跟這個有關,如下面這段程序:
#include<stdio.h>
int main(void)
{
int a[3]={1,2,3};
int *p=(a+3);
printf("%d\n",p[-1]);
return 0;
}
程序執行結果為3,雖然下標為-1,但是被編譯器解析為偏移量,因此相當於*(p-1)。
規則3:在函數參數的聲明中,數組名被編譯器當做指向該數組第一個元素的指針。在C語言中將形參的數組和指針等同起來是出於效率的考慮。假如不這麼做,將整個數組的每個元素的值都拷貝一份進行傳遞,這樣無論在時間上還是空間上的開銷都可能是非常大的。但是又要能操作到數組中的元素,只需將數組第一個元素的地址傳遞給調用函數,然後通過指針去訪問想要訪問的空間,這樣一來時空消耗將大大減少。因此在函數內部,編譯器始終把參數中聲明的數組名當做一個指向數組第一個元素的指針,這樣一來,編譯器可以產生正確代碼,並不需要對數組和指針這兩種情況作區分。因此void fun(int a[]);和void fun(int *a)兩種形式的效果完全等同,在函數內部去引用a的話,始終都會被編譯器認為是指針。因為void fun(int a[]);這種形式最終還是會被編譯器解析為void fun(int *a);這種形式告訴我們調用時必須傳遞一個指向整型數據的指針。所以下面這段代碼可以正確編譯和執行:
#include<stdio.h>
void fun(int a[])
{
printf("%d\n",a[0]);
}
int main(void)
{
int a[3]={1,2,3};
int *p1,*p2;
int b=4;
p1=a;
p2=&b;
fun(a);
fun(&a[1]);
fun(p1);
fun(p2);
fun(&b);
return 0;
}
區分幾個表達式的含義:
&p,p,a,&a
&p:表示取存儲指針變量p的內存單元的地址; sizeof(&p)=4;
p:表示指針變量p存儲的地址; sizeof(p)=4;
a:表示數組第一個元素的地址; sizeof(a)=3*4=12;
&a:表示整個數組的首地址; sizeof(&a)=4(在VC++6.0中該值為12,我認為是錯誤的,因為其類型是數組指針)
雖然a和&a的值相同,但是所表達的含義完全不同,a表示數組第一個元素的地址,而&a表示數組的首地址。它們所代表的類型也完全不同,a是一個int型指針,而&a是一個int (*p)[]型指針,即數組指針(在後續文章中會作解釋)。所以a+1和&a+1得到的結果不同,a+1表示將指向該數組的第一個元素的指針向後移一個步長(這裡的步長為數組元素類型所占的字節數);而&a+1表示將指向該數組的指針向後移動一個步長(而此處的步長為數組元素個數*元素類型所占的字節數)。
#include<stdio.h>
int main(void)
{
int a[3]={1,2,3};
int *p=a;
printf("%08x\n",&p);
printf("%08x\n",p);
printf("%08x\n",&p+1);
printf("%08x\n",p+1);
printf("%08x\n",a);
printf("%08x\n",&a);
printf("%08x\n",a+1);
printf("%08x\n",&a+1); //注意輸出結果
return 0;
}
作者 海子