一、指針
由於指針保存的數據都是地址,所以無論什麼類型的指針都佔4字節內存空間。
①各類型指針訪問同一地址
每種數據類型在內存中所佔的內存空間不同,指針中只保存了存放數據的首地址,而沒有指明在哪裏結束。這時需要根據對應的類型來尋找解釋數據的結束地址。
先看看C++代碼
int main() { int num=0x12345678; int *pn=# char *pc=(char *)# short *psn=(short *)# long long *pln=(long long *)# cout<<hex<<static_cast<int>(*pn)<<endl; cout<<hex<<static_cast<int>(*pc)<<endl; cout<<hex<<static_cast<short>(*psn)<<endl; cout<<hex<<static_cast<long long>(*pln)<<endl; system("pause"); return 0; }
運行結果:
再看反彙編
int num=0x12345678; 011E13EE mov dword ptr [num],12345678h int *pn=# 011E13F5 lea eax,[num] //取的都是num的地址 011E13F8 mov dword ptr [pn],eax char *pc=(char *)# 011E13FB lea eax,[num] 011E13FE mov dword ptr [pc],eax short *psn=(short *)# 011E1401 lea eax,[num] 011E1404 mov dword ptr [psn],eax long long *pln=(long long *)# 011E1407 lea eax,[num] 011E140A mov dword ptr [pln],eax cout<<hex<<static_cast<int>(*pn)<<endl; 011E140D mov esi,esp 011E140F mov eax,dword ptr [__imp_std::endl (11E82B0h)] 011E1414 push eax 011E1415 mov edi,esp 011E1417 mov ecx,dword ptr [pn] //將指針pn中存放的變量num的地址放入ecx 011E141A mov edx,dword ptr [ecx] //從變量num的地址中,以4字節方式讀取數據,存入edx中 011E141C push edx ......//輸出省略 cout<<hex<<static_cast<int>(*pc)<<endl; 011E1455 mov esi,esp 011E1457 mov eax,dword ptr [__imp_std::endl (11E82B0h)] 011E145C push eax 011E145D mov ecx,dword ptr [pc] 011E1460 movsx edx,byte ptr [ecx] //從變量num的地址中,以1字節方式讀取數據(int的第一個字節),存入edx中 011E1463 mov edi,esp 011E1465 push edx ......//輸出省略 cout<<hex<<static_cast<short>(*psn)<<endl; 011E149E mov esi,esp 011E14A0 mov eax,dword ptr [__imp_std::endl (11E82B0h)] 011E14A5 push eax 011E14A6 mov edi,esp 011E14A8 mov ecx,dword ptr [psn] 011E14AB movzx edx,word ptr [ecx] //從變量num的地址中,以2字節方式讀取數據(int的低2字節),存入edx中 011E14AE push edx ......//輸出省略 cout<<hex<<static_cast<long long>(*pln)<<endl; 011E14E7 mov esi,esp 011E14E9 mov eax,dword ptr [__imp_std::endl (11E82B0h)] 011E14EE push eax 011E14EF mov edi,esp 011E14F1 mov ecx,dword ptr [pln] //這裏存放的儘管依然是num的地址,但是num被強制轉換爲long long型,所以後面讀取的是8字節的數據,但寄存器只能存放4字節數據,
//所以分兩次讀取。int4字節擴展到long long8字節,高4字節中的值是未知的 011E14F4 mov edx,dword ptr [ecx+4] //從num地址+4的地址中,以4字節方式讀取數據(long long型的高4字節),放入edx中 011E14F7 push edx 011E14F8 mov eax,dword ptr [ecx] //從num地址中,以4字節方式讀取數據(long long型的低4字節),放入eax中 011E14FA push eax ......//輸出省略
②各類型指針的尋址方式
指針的取內容操作分爲兩個步驟:先取指針中保存的地址,然後針對這個地址進行取內容,也就是一個間接尋址的過程,這也是識別指針的重要依據。
C++中,所有指針類型只支持加減法,其他運算對於指針而言沒有意義。
指針加減用於地址偏移。指針加減1後,指針內保存的地址值並不一定加減1,具體的值取決於指針類型。
兩指針相加沒有意義,相減是計算兩個地址之間的元素個數,結果爲有符號整數,進行減法操作的兩個指針必須是同類型指針相減。
C++代碼
int main() { char num[8]={0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48}; int *pnum=(int *)num; short *psnum=(short *)num; char *pcnum=num; long long *plnum=(long long *)num; pnum+=1; psnum+=1; pcnum+=1; plnum+=1; cout<<hex<<static_cast<int>(*pnum)<<endl; cout<<hex<<static_cast<short>(*psnum)<<endl; cout<<hex<<static_cast<char>(*pcnum)<<endl; cout<<hex<<static_cast<long long>(*plnum)<<endl; system("pause"); return 0; }
運行結果: 高高低低原則的結果
反彙編:
char num[8]={0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48}; 004014D8 mov byte ptr [ebp-10h],41h //爲數組元素賦初值。EBP-10H,即是數組首元素的地址,又是數組地址 004014DC mov byte ptr [ebp-0Fh],42h 004014E0 mov byte ptr [ebp-0Eh],43h 004014E4 mov byte ptr [ebp-0Dh],44h 004014E8 mov byte ptr [ebp-0Ch],45h 004014EC mov byte ptr [ebp-0Bh],46h 004014F0 mov byte ptr [ebp-0Ah],47h 004014F4 mov byte ptr [ebp-9],48h int *pnum=(int *)num; 004014F8 lea eax,[ebp-10h] //賦值都是數組首地址 004014FB mov dword ptr [ebp-1Ch],eax short *psnum=(short *)num; 004014FE lea eax,[ebp-10h] 00401501 mov dword ptr [ebp-28h],eax char *pcnum=num; 00401504 lea eax,[ebp-10h] 00401507 mov dword ptr [ebp-34h],eax long long *plnum=(long long *)num; 0040150A lea eax,[ebp-10h] 0040150D mov dword ptr [ebp-40h],eax pnum+=1; 00401510 mov eax,dword ptr [ebp-1Ch] //將指針pnum內存放的地址放入eax(num的地址),然後num的地址+4(int),再放入pnum指針,此時指向索引爲4的元素的地址 00401513 add eax,4 00401516 mov dword ptr [ebp-1Ch],eax psnum+=1; 00401519 mov eax,dword ptr [ebp-28h] //將指針psnum內存放的地址放入eax(num的地址),然後num的地址+2(short),再放入psnum指針,此時指向索引爲2元素的地址 0040151C add eax,2 0040151F mov dword ptr [ebp-28h],eax pcnum+=1; 00401522 mov eax,dword ptr [ebp-34h] //將指針pcnum內存放的地址放入eax(num的地址),然後num的地址+1(char),再放入pcnum指針,此時指向索引爲1的元素的地址 00401525 add eax,1 00401528 mov dword ptr [ebp-34h],eax plnum+=1; 0040152B mov eax,dword ptr [ebp-40h] //將指針plnum內存放的地址放入eax(num的地址),然後num的地址+8(long),再放入pcnum指針,此時指向最後一個元素的後面的地址 0040152E add eax,8 00401531 mov dword ptr [ebp-40h],eax cout<<hex<<static_cast<int>(*pnum)<<endl; 00401534 mov esi,esp 00401536 mov eax,dword ptr [__imp_std::endl (40A324h)] 0040153B push eax 0040153C mov edi,esp 0040153E mov ecx,dword ptr [ebp-1Ch] 00401541 mov edx,dword ptr [ecx] //讀取的是第5個元素地址開始的4個字節 00401543 push edx ......//省略輸出 cout<<hex<<static_cast<short>(*psnum)<<endl; 0040157C mov esi,esp 0040157E mov eax,dword ptr [__imp_std::endl (40A324h)] 00401583 push eax 00401584 mov edi,esp 00401586 mov ecx,dword ptr [ebp-28h] 00401589 movzx edx,word ptr [ecx] //讀取的是第3個元素地址開始的2個字節 0040158C push edx ......//省略輸出 cout<<hex<<static_cast<char>(*pcnum)<<endl; 004015C5 mov esi,esp 004015C7 mov eax,dword ptr [__imp_std::endl (40A324h)] 004015CC push eax 004015CD mov ecx,dword ptr [ebp-34h] 004015D0 movzx edx,byte ptr [ecx] //讀取的是第2個元素地址開始的1個字節 004015D3 push edx ......//省略輸出 cout<<hex<<static_cast<long long>(*plnum)<<endl; 00401606 mov esi,esp 00401608 mov eax,dword ptr [__imp_std::endl (40A324h)] 0040160D push eax 0040160E mov edi,esp 00401610 mov ecx,dword ptr [ebp-40h] 00401613 mov edx,dword ptr [ecx+4] //讀取的是第8個元素後面的地址開始的8個字節,是未知的值。 00401616 push edx 00401617 mov eax,dword ptr [ecx] 00401619 push eax ......//省略輸出
......
二、引用
引用類型是變量的別名。C++爲了簡化指針操作,對指針操作進行了封裝,產生了引用類型。實際上引用類型就是指針類型,只不過它用於存放地址的內存空間對使用者而言是隱藏的,通過編譯器實現尋址,而指針需要手動尋址,其存儲方式也是和指針一樣,都是使用內存空間存放地址值。反彙編下,沒有引用這種類型。
看一下引用類型作爲函數參數,和指針完全一樣。
C++代碼
void add(int &b) { b++; } int main() { int a=1; add(a); cout<<a<<endl; system("pause"); return 0; }
運行結果:2
反彙編
int a=1; 00EF140E mov dword ptr [a],1 add(a); 00EF1415 lea eax,[a] //將變量a的地址放入eax 00EF1418 push eax //把a的地址放入堆棧,傳遞給add 00EF1419 call add (0EF1154h) //調用add函數 00EF141E add esp,4 ......省略輸出
add函數
b++; 00EF13CE mov eax,dword ptr [b] //這裏的b實際上是指向堆棧中變量a的地址,將b中存放的地址也就是變量a的地址放入eax,debug下,爲了調試直觀,直接用b表示了。 00EF13D1 mov ecx,dword ptr [eax] //將eax也就是變量a地址中的內容放入ecx,然後ecx+1,再放入變量a的地址中 00EF13D3 add ecx,1 00EF13D6 mov edx,dword ptr [b] //將堆棧中變量a的地址放入edx 00EF13D9 mov dword ptr [edx],ecx //將ecx++後的值放入變量a的地址中。
.............