博客轉載請註明原地址: http://blog.csdn.net/sunliymonkey/article/details/48139183
問題:在c/c++語言中,爲什麼c[5] == 5[c]?
這個問題,當初是在德問上看見的,起初自己也不知道其機理,猜測與c語言的編譯機制有關,於是通過反彙編、猜測、驗證,最終找到了原由。
下面是我分析該問題的過程,首先來看一段關於數組的代碼:
#include<iostream>
using namespace std;
int main()
{
int a[5];
for(int i = 1; i < 5; i++)
{
a[i] = i + 5;
}
a[3] = 11;
3[a] = 15;
cout << a[3] << endl;
system("pause");
return 0;
}
使用Microsoft Visual 2010對代碼進行反彙編:
7: {
8: a[i] = i + 5;
00251B80 8B 45 D8 mov eax,dword ptr [i]
00251B83 83 C0 05 add eax,5 // eax = i + 5
00251B86 8B 4D D8 mov ecx,dword ptr [i] // 用ecx存放a[i]下標i的值
00251B89 89 44 8D E4 mov dword ptr [ebp+ecx*4-1Ch],eax // 將eax的值傳給a[i]
9: }
根據上面的彙編代碼,我們可以發現編譯器對於a[i]
的彙編表示:
a[i]: dword ptr [ebp+ecx*4-1Ch]
其中dword
,表示雙字(四個字節);ptr
,pointer縮寫,表示指針;[addr]
中的addr
表示地址位置。整個式子表示從位於ebp+ecx*4-1Ch
的地址處,提取變量大小爲4個字節的變量值,與a[i]
進行對比,可以猜測:
a[i] <---> [ebp+ecx*4-1Ch]
a <---> ebp - 1Ch //數組首地址
i <---> ecx //數組下標
int <---> 4 //變量大小
通過Microsoft Visual 2010對比a
與ebp-1Ch
的值,可以發現它們相等,也就證明了我們上面的對應關係,總結出編譯器對於數組的解釋:
ptr [數組首地址 + 數組下標 * sizeof(變量類型)]
貌似這個結論,比較顯然,但是如果按照這個結論來看:
a[3]翻譯爲: ptr [ a + 3 * sizeof(int) ]
3[a]翻譯爲: ptr [ 3 + a * sizeof(int) ]
如此來看,兩者不應該是一樣的,但是實際運行程序發現,兩者確實相等。這讓我們感覺到編譯器非常聰明,能夠識別出
a
纔是真正的數組首地址,3
纔是真正的數組下標。考慮到編譯器的才能是由我們程序員所賦予,顯然其無法真正識別出誰是數組首地址,而是通過預設的指導規則得到。在這裏,
3
與a
,對編譯器來講,前者是一個常數,後者是一個指針變量。從更高層次來看a[i]
與[i]a
,兩者均爲變量,不過其中一個是整數類型,另外一個是指針類型。能夠猜測到,編譯器應該是將指針變量識別爲數組首地址,而剩餘的變量值作爲數組下標。
接下來我們做以下實驗進行驗證:
int a[5];
int *p = a;
int i = 2 , b = &a;
//a: 數組首地址
i[a] = 1; // 正確
2[a] = 1; // 正確
(i+2)[a+1]=1; // 正確
//p: int指針變量
i[p] = 1; // 正確
2[p] = 1; // 正確
(i+2)[p+1]=1; // 正確
//無指針變量
b[2] = 3; // 失敗 error C2109: 下標要求數組或指針類型
2[b] = 4; // 失敗 error C2109: 下標要求數組或指針類型
i[b] = 5; // 失敗 error C2109: 下標要求數組或指針類型
b[i] = 6; // 失敗 error C2109: 下標要求數組或指針類型
因此編譯器對於數組的處理時,必須要有一個指針變量作爲基址,其它數值作爲數組下標。你可能不禁會問:如果存在兩個指針變量,怎麼辦? 按照上面的推測,存在兩個指針變量,編譯器貌似是無法處理的,實驗驗證也表明,存在多個指針變量,將會報錯:
int a[5],b[5];
a[b+2] = 4; // 失敗 error C2107: 非法索引,不允許間接尋址
至此問題分析完畢,總結如下:
a[i] 與 i[a]的含義一樣
數組a[i]被解釋爲: ptr [數組首地址 + 下標 * sizeof(變量類型)]
表達式中必須出現一個指針變量,無論其位置如何,其被視爲數組首地址,剩餘的值作爲數組下標