在很多人眼裏,C語言的指針和數組是等價的。幾乎在任何時候數組都可以指針來替換,這使得很多人把這兩個類型視作等價。其實完全不同。數組中存的是數據,指針中存的是地址。我們造成這樣的誤解是有情可緣的,對於int *a而言,a是一個變量,這個變量的值是一個4字節整數的地址。而對於int a[10]而言,我們常常可以在一些書上看到a指向數組的第一個元素,似乎它也是個指針變量,變量的值是a[0]的地址。這與int *a似乎完全一樣。但其實不然:看下面一段程序:
- int a[10];
- int main(){
- int *p = a;
- p[5] = 4;
- return p[5];
- }
翻譯出的彙編可以看到
movl $4, -24(%ebp)
對於一個數組char a[10]而言,如果訪問a[i],步驟是:
1. 將a的地址讀入寄存器,由於a其實就是一個別名,所以a的地址本身就是a[0]的地址
2. 根據類型計算並添加偏移,得到目標地址
3. 訪問內存得到a[i]
而如果聲明是char *a,訪問a[i],其步驟是:
1. 將a的地址讀入寄存器,a是一個指針,指向a[0]的地址。
2. 訪問內存讀取a的值,得到a[0]的地址。
3. 根據類型計算並添加偏移量,得到目標地址
4. 訪問內存得到a[i]
根據這樣的理解,當我們在一個文件中聲明變量爲char *a,而在另一個文件中將a定義爲數組,即char a[10],將會造成非常大的問題。在聲明變量的文件中使用a[i],將首先會把a的地址讀入寄存器,訪問內存取出a的值,而由於其定義爲數組,因此a的地址就是a[0]的地址,將取出的a[0]作爲目標地址的基址顯然是錯誤的。
那麼肯定有人會問,int *p = a算是個什麼東西,不是說好的a是個別名而已麼,a的地址纔是a[0]的地址啊。其原因是,
C標準中說道,在表達式中使用數組,編譯器會將數組自動轉化成指針,這是所有C編譯器必須有的行爲,該指針指向數組的第一個元素。需要注意的是,對於指針的下標操作[],當且僅當其定義是數組的時候纔有效,對於指針類型的聲明,編譯器將下標操作直接作爲加號操作,因此a[5]和5[a]在a爲指針的時候都是合法的。可能又會有人有疑問,常常看到一些函數的簽名是指針,可傳入的是數組,不也沒事兒麼。C標準還有一條:當數組出現在函數的參數中時,無論是形參還是實參,都轉換爲指針再進行操作。因此main函數的參數char *argv[],也可以寫成char **argv.但這種轉換不是在任何時候都成立的,比如多維數組的情形,char a[3][4][5]作爲參數傳遞的時候只會轉換爲一個3維數組的指針char (*a)[4][5],而不會轉成指針的指針的指針。函數不可以返回數組,但可以返回數組的指針,比如: