指針與引用(反彙編)

一、指針

由於指針保存的數據都是地址,所以無論什麼類型的指針都佔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=&num;
011E13F5  lea         eax,[num]  //取的都是num的地址
011E13F8  mov         dword ptr [pn],eax  
    char *pc=(char *)&num;
011E13FB  lea         eax,[num]  
011E13FE  mov         dword ptr [pc],eax  
    short *psn=(short *)&num;
011E1401  lea         eax,[num]  
011E1404  mov         dword ptr [psn],eax  
    long long *pln=(long long *)&num;
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的地址中。
複製代碼

.............

 

好好學習,天天向上!!!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章