指针与引用(反汇编)

一、指针

由于指针保存的数据都是地址,所以无论什么类型的指针都占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的地址中。

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

时间: 2024-11-18 02:13:02

指针与引用(反汇编)的相关文章

指针与引用

int main() { 000000013FA01010 push rdi 000000013FA01012 sub rsp,50h 000000013FA01016 mov rdi,rsp 000000013FA01019 mov ecx,14h 000000013FA0101E mov eax,0CCCCCCCCh 000000013FA01023 rep stos dword ptr [rdi] int x=0x12345678; 000000013FA01025 mov dword p

#详解C++中的指针与引用

指针和引用形式上很好区别,但是他们似乎有相同的功能,都能够直接引用对象,对其进行直接的操作.但是什么时候使用指针?什么时候使用引用呢?这两者很容易混淆,在此介绍一下指针和引用,力争将最真实的一面展现给大家. 1.指针和引用的定义 在深入介绍之前我们首先来看一下指针和引用的定义.指针和引用的区别,然后分别针对指针和引用展开讨论,深入细节为何有这些差异. 指针的权威定义: In a declaration T D where D has the form * cv-qualifier-seqopt

C++中的指针和引用

此文来自Listening_music:   http://blog.csdn.net/listening_music/article/details/6921608 感谢作者提供的美文,本人很受用,谢谢! 1.指针和引用的定义 下面用通俗易懂的话来概述一下指针和引用: 指针-对于一个类型T,T*就是指向T的指针类型,也即一个T*类型的变量能够保存一个T对象的地址,而类型T是可以加一些限定词的,如const.volatile等等.见下图,所示指针的含义: 引用-引用是一个对象的别名,主要用于函数

从Java到C++——指针与引用

C++即有指针又有引用,而且很多时候具有相似的功能,常常容易让人混淆,着实让人头痛.用两句通俗的话说明两者本质的含意:引用就是一个变量或对象的别名(引用的本质是一个对象):指针是一个段内存空间的地址(指向存储一个变量值的空间或一个对象的空间):如下图所示:     C++中的引用与指针 引用 引用是为变量或对象起的另外一个别名,定义形式:T& v;  T为类型,v为变量名.使用引用时,注意以下几点: 1.引用在创建的时候必须被初始化(指针可以在任何时候赋值) 2.一旦引用初始化后,就不能改变引用

C++指针参数引用

粘个代码占位置,以后有时间把指针函数,函数指针都补上 #include <iostream>using namespace std;void freePtr1(int* p1){    /*未释放内存前 ->  p1 Address : 0012FDDC  p1 value : 003429B8,     在这里,p1它也是一个变量,既然是一个变量,     那么它将会以值的传递,把外部变量p1传到栈内,在栈内产生一个地址:0012FDDC,     当然,它的值不会变仍然是指向堆地址:

c++指针与引用问题

本来是回答问题的,到这里做个笔记 *&L是指针的引用,实参是个指针.所以L是实参指针的别名,对别名L的修改,等于对实参的修改.*L是传值,你无法改变传过来的实参指针变量的值程序代码: #include<iostream>using namespace std;void foo(int*p);int main(){    int a=5;    int *b=&a;    printf("%d %d\n",b,*b);    foo(b);    printf

(C/C++)区别:数组与指针,指针与引用

1.数组跟指针的区别 数组要么在静态存储区被创建(如全局数组),要么在栈上被创建.数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变. 指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存.指针远比数组灵活,但也更危险. 数组和指针特点的简单比较: 数组 指针 保存数据 保存地址 直接访问数据 间接访问数据,先取得指针的内容,然后以它为地址取得数据 用于存储数目固定且类型相同的数据 通常用于动态数据结构 编译器自动分配和删除

指针与引用同与不同

一.指针与引用的定义及特性 A.引用(reference):为一个已经存在的对象另起一个名字.它有以下特性: 1.必须初始化 2.始终指向同一个对象,不能更改其所指向的对象 3.自身只是一个别名,不占内存,对其取地址是其指向对象的地址 4.常量引用的意义在于不能通过引用来修改其所指向对象的值,其所指对象可以不是常量 int tempA = 2; const int ctempA = 2; /*1.正常使用*/ int &refA = tempA; const int &crefA = ct

[C/C++基础--笔试突击] 7.指针与引用

概述: 比较抽象的但又很有用的东西 0.0 void*指针:可以保存任何类型对象的地址. 指向指针的指针 函数指针 7.1 指针 一个有效的指针必然是一下三种状态之一: 1)保存一个特定对象的地址: 2)指向某个对象后面的另一个对象 3)0值. 若指针保存0值,表明它不指向任何对象.未初始化的指针是无效的,直到给该指针赋值后,才可使用. 注:*p++和(*p)++不等价,单目运算符*的优先级比++高,故*p++先完成取值操作,然后对指针地址执行++操作,而(*p)++是首先执行屈指操作,然后对该

指针和引用的区别(More Effective c++ )

指针与引用看上去完全不同(指针用操作符"*"和"->",引用使用操作符". " ),但 是它们似乎有相同的功能.指针与引用都是让你间接引用其他对象.你如何决定在什么时候 使用指针,在什么时候使用引用呢? 首先,要认识到在任何情况下都不能使用指向空值的引用.一个引用必须总是 指向某些 对象 .因此如果你使用一个变量并让它指向一个对象, 但是该变量在某些时候也可能不指向 任何 对象,这时你应该把变量声明为指针,因为这样 你可以赋空值给该变量.相