在反匯編觀察后,你會發(fā)現(xiàn):指針變量就是塊內(nèi)存區(qū)域,里面存放的是地址,你可以通過這個地址訪問其它內(nèi)存。
數(shù)組就是塊連續(xù)的內(nèi)存區(qū)域,里面連續(xù)排列著同樣size的內(nèi)存,多維數(shù)組也是一樣的。
上述很簡單,就不貼代碼贅述了。
但人們一般糾結(jié)這樣一個問題:數(shù)組名 是不是 一種指針?
也就是說arr[]的這個arr是不是一種指針?
這個問題之前csdn論壇上討論的熱火朝天:
http://bbs.csdn.net/topics/380226723
http://blog.csdn.net/yby4769250/article/details/7294718#reply
一種看法就是把這個數(shù)組名當(dāng)作一種特殊的指針來看待,特殊之處在于是常量,不能改變其指向的位置。
一種是透過C語言,從C語言編譯之后的匯編,視角來看,認(rèn)為數(shù)組名和指針在內(nèi)存中完全不是一回事。
太認(rèn)真考慮其定義和概念就接近于一種“玄學(xué)”,我這里就單純講反匯編角度來分析 數(shù)組名 和 指針在內(nèi)存中的差別吧。
隨便寫的代碼:
#include <stdio.h> void func(int *p) { *(p+1) = 2;} void main() { int v = 1; int *p = &v; int v2 = *p + v; int arr[100]; int v3 = arr[0] + v; int v4 = *(arr+2) + v2; func(arr);}
void main() {008114D0 push ebp 008114D1 mov ebp,esp 008114D3 sub esp,298h 008114D9 push ebx 008114DA push esi 008114DB push edi 008114DC lea edi,[ebp-298h] 008114E2 mov ecx,0A6h 008114E7 mov eax,0CCCCCCCCh 008114EC rep stos dword ptr es:[edi] 008114EE mov eax,dword ptr ds:[00818000h] 008114F3 xor eax,ebp 008114F5 mov dword ptr [ebp-4],eax int v = 1;008114F8 mov dword ptr [v],1 int *p = &v;008114FF lea eax,[v] 00811502 mov dword ptr [p],eax int v2 = *p + v;00811505 mov eax,dword ptr [p] 00811508 mov ecx,dword ptr [eax] 0081150A add ecx,dword ptr [v] 0081150D mov dword ptr [v2],ecx int arr[100]; int v3 = arr[0] + v;00811510 mov eax,4 00811515 imul eax,eax,0 00811518 mov ecx,dword ptr arr[eax] 0081151F add ecx,dword ptr [v] 00811522 mov dword ptr [v3],ecx int v4 = *(arr+2) + v2;00811528 mov eax,dword ptr [ebp-1B4h] 0081152E add eax,dword ptr [v2] 00811531 mov dword ptr [v4],eax func(arr);00811537 lea eax,[arr] 0081153D push eax 0081153E call func (0811226h) 00811543 add esp,4 }
可以看到arr[100]這個數(shù)組名arr在匯編代碼中變成了一個單純的標(biāo)號,代表一個地址,這個地址是 數(shù)組這一列連續(xù)內(nèi)存空間的首地址。
假設(shè)數(shù)組地址是N,那么數(shù)組名在匯編代碼中就是N本身。
但對比來看,指針在這里是棧空間上申請的內(nèi)存。這塊內(nèi)存里存著別的內(nèi)存的地址。
假設(shè)指針的地址是M,指針指向的內(nèi)存的地址是N,那就是地址為M的內(nèi)存里存著N。
二者對比 數(shù)組名和指針的差別就清楚了吧。一個就只是標(biāo)號(地址),一個是一塊內(nèi)存,內(nèi)存里面存著地址。
上述是講數(shù)組名 和 指針的差別。
下面要講,
而當(dāng)數(shù)組作為函數(shù)參數(shù)時,其實就是轉(zhuǎn)化為指針來玩的。
先講調(diào)用函數(shù)時的反匯編代碼(這里取數(shù)組地址,然后壓入棧再call func):
func(arr); 00811537 lea eax,[arr] 0081153D push eax 0081153E call func (0811226h) 00811543 add esp,4
再講func函數(shù)里面對數(shù)組操作的反匯編代碼:
上面的代碼的函數(shù)func:
void func(int *p) { *(p+1) = 2; }
反匯編代碼為:
void func(int *p) { 00FA3DE0 push ebp 00FA3DE1 mov ebp,esp 00FA3DE3 sub esp,0C0h 00FA3DE9 push ebx 00FA3DEA push esi 00FA3DEB push edi 00FA3DEC lea edi,[ebp-0C0h] 00FA3DF2 mov ecx,30h 00FA3DF7 mov eax,0CCCCCCCCh 00FA3DFC rep stos dword ptr es:[edi] *(p+1) = 2; 00FA3DFE mov eax,dword ptr [p] 00FA3E01 mov dword ptr [eax+4],2 } 00FA3E08 pop edi 00FA3E09 pop esi 00FA3E0A pop ebx 00FA3E0B mov esp,ebp 00FA3E0D pop ebp 00FA3E0E ret
這里形參為指針,沒什么異議。
改寫一下,形參改為,arr[]的話:
void func(int arr[]) { arr[1] = 2; }
void func(int arr[]) { 01173DE0 push ebp 01173DE1 mov ebp,esp 01173DE3 sub esp,0C0h 01173DE9 push ebx 01173DEA push esi 01173DEB push edi 01173DEC lea edi,[ebp-0C0h] 01173DF2 mov ecx,30h 01173DF7 mov eax,0CCCCCCCCh 01173DFC rep stos dword ptr es:[edi] arr[1] = 2; 01173DFE mov eax,4 01173E03 shl eax,0 01173E06 mov ecx,dword ptr [arr] 01173E09 mov dword ptr [ecx+eax],2 } 01173E10 pop edi 01173E11 pop esi 01173E12 pop ebx 01173E13 mov esp,ebp 01173E15 pop ebp 01173E16 ret
可以看出二者(不管形參寫成*p還是arr[])是沒差別的。
數(shù)組作為函數(shù)參數(shù),都是會被轉(zhuǎn)化為指針來操作。
為何?
因為調(diào)用函數(shù)前壓入棧的是數(shù)組的地址,而不是整個數(shù)組。這個地址存在??臻g里,占有一定內(nèi)存,所以成了一個指針!
函數(shù)里面從棧里獲取到這個指針,繼續(xù)操作。
這樣是有一定道理的,因為數(shù)組可能很長,如果整個壓入棧就是從新復(fù)制了一份空間,可能非常浪費,還不如指向前一個??臻g里數(shù)組的內(nèi)存。
最后我想說,我玩這個反匯編,似乎沒啥實際用處,比如就算不懂?dāng)?shù)組名 和 指針差別,也可以寫程序啊。
但我總覺得,作為一個程序員,搞明白自己寫的程序,到底是怎么被計算機(jī)運行的,會非常TMD爽。而不只是單純的會寫。
所以我最近決定系統(tǒng)的玩一把反匯編。
讓自己對自己寫的任何一行C、C++代碼都對其運行原理、內(nèi)存都一清二楚,看到C、C++代碼就能迅速想象出匯編代碼的樣子。
下一個課題:徹底搞明白,各種數(shù)據(jù)類型在內(nèi)存中的存儲,又要涉及補碼了。hhh
http://www.cnblogs.com/rixiang/p/7121838.html