首页 > 代码库 > 指针与引用(反汇编)

指针与引用(反汇编)

一、指针

由于指针保存的数据都是地址,所以无论什么类型的指针都占4字节内存空间。

①各类型指针访问同一地址

每种数据类型在内存中所占的内存空间不同,指针中只保存了存放数据的首地址,而没有指明在哪里结束。这时需要根据对应的类型来寻找解释数据的结束地址。

先看看C++代码

int main(){    int num=0x12345678;    int *pn=&num;    char *pc=(char *)&num;    short *psn=(short *)&num;    long long *pln=(long long *)&num;    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的地址放入ecx011E141A  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的地址放入eax00EF1418  push        eax  //把a的地址放入堆栈,传递给add00EF1419  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的地址放入edx00EF13D9  mov         dword ptr [edx],ecx  //将ecx++后的值放入变量a的地址中。

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

 

指针与引用(反汇编)