反汇编(一)C/C++变量、常量、指针和引用在内存中的表现形式

高级语言隐藏了太多信息。这一篇用汇编来直观探究 C/C++ 变量,函数,类,继承等等再内存中是怎么表示的

先来看这样一段代码,分为三个部分。 先准备好三个部分准备反汇编。

1. defineVar() 函数内解释 变量在内存中的表现形式

2. varPtr() 函数内解释指针与引用在内存中的表现形式

3.constdefine() 函数内解释const与define的区别跟在内存中的表现形式

#include<cstdio>
//define与const在内存中的形式
void constdefine()
{
	#define PI 3
	const int a = 5;
	const int* const bs = &a;
	int P = PI;
}
//指针与引用在内存中的表现形式
void varPtr()
{
	int a = 0x0F;
	int * ptr = &a;	
	char * pt = (char*)ptr;
	int & sbs = a;	
} 
//各变量在内存中的表现形式
void defineVar()
{
	int a = 4;
	int b = 5;
	int c = 6;
	float abc = 4.5f;
	double lop = 8.9;
}
int main()
{

	defineVar();
	varPtr(); 
	constdefine();
	return 0;
} 

 接下来直接OD(当然也可以选择VS的反汇编窗口),我这里选择了OD。可以忽略这张图,直接看下面(给出了剪切反汇编代码)

先看第一部分(函数体内,已经粘贴出来):

解释在反汇编代码中,首先看defineVar()函数



                        //推入栈中,保存EBP的值
004013ED                PUSH EBP
                        //用EBP保存ESP的调用初值  
004013EE                MOV EBP,ESP
                        //分配变量空间,我们可以计算一下
                        //defineVar()中有5个变量
                        //其中3个四字节变量,float在内存中
                        //占8个字节,double占8个字节
                        //那么一共28字节,换算成16进制是1C
                        //那么为什么会分配32(20H)字节呢?
                        //因为main函数在调用这个函数的时候
                        //push了一个4字节的地址在栈中(函
                        //数的返回地址)
                        //这里为了内存对齐(提高效率),所以
                        //编译器多分配了4个字节
004013F0                SUB ESP,20
                                                      
                        //int a = 4;
                        //可以看到内存中只有地址,没有变量                
                        //名,变量名只是给程序员看的
004013F3                MOV DWORD PTR SS:[EBP-4],4
                                            
                        //int b = 5;

004013FA                MOV DWORD PTR SS:[EBP-8],5
                                                    
                        //int c = 6;  

00401401                MOV DWORD PTR SS:[EBP-C],6

                        //这里是小数赋值。没有用到浮点数
                        //指令跟SSE指令(扩展指令集浮点数                
                        //运算)
                        //浮点数编码形式 参见 IEEE编码
                        //float abc = 4.5f
00401408                MOV EAX,DWORD PTR DS:[403064]
0040140D                MOV DWORD PTR SS:[EBP-10],EAX


                        //double 精度比float高。
                        //采用两个32位寄存器存储

00401410                MOV EAX,CCCCCCCD
00401415                MOV EDX,4021CCCC
            
                        //double lop = 8.9
0040141A                MOV DWORD PTR SS:[EBP-18],EAX
0040141D                MOV DWORD PTR SS:[EBP-14],EDX

                        //恢复调用函数时 EBP的状态
00401420                LEAVE 
                        //销毁变量 return
00401421                RETN

变量的定义用一张图总结就是:

在看varPtr()这个函数的反汇编

                   //后三步都跟前面一样
                   //至于为什么不按顺序存储数据,目前
                   //我不知道   
004013CC           PUSH EBP
004013CD           MOV EBP,ESP
004013CF           SUB ESP,10
                                                      
                   //int a = 0x0F;
004013D2           MOV DWORD PTR SS:[EBP-10],0F
                                                      
                   //用lea指令取到a的地址放在ptr中(也就是EBP-4),注意是地址(4字节)

004013D9           LEA EAX,DWORD PTR SS:[EBP-10]
004013DC           MOV DWORD PTR SS:[EBP-4],EAX

                   //char * pt = (char*)ptr;
                   //这一步可以看出,其实没有真正的强
                   //制转换(还是4字节的地址),那么这一步的"强转"只是改变了计算机对这一
                   //段数据的处理方式

004013DF           MOV EAX,DWORD PTR SS:[EBP-4]
004013E2           MOV DWORD PTR SS:[EBP-8],EAX

                   //int & sbs = a;	
                   //可以看到引用其实跟指针是一样的。
                   //只不过引用一开始就绑定了一个地址
                   //往后就不能改变了(除了栈解退)
  
004013E5           LEA EAX,DWORD PTR SS:[EBP-10]
004013E8           MOV DWORD PTR SS:[EBP-C],EAX
                   //这个是伪指令,就是等同于},销毁
                   //栈中变量,恢复EBP,注意第一步有          
                   //push EBP,对应起来
004013EB           LEAVE 
                                                    
                   //return。函数返回调用者 恢复        
                   //堆栈平衡                    
004013EC           RETN  

在看第三部分,constdefine()函数。首先我们并没有看到有关define的语句。

其实define语句是预处理,在编译之前预处理器对文件进行了预先处理。会生成一个XXX.i的文件

在这个文件中,预处理器已经把用到define的地方都进行了文本替换,由于条件关系,这里不列出了。

在看这个const对变量的影响。好像也没什么影响。

可以这么说,define才是真正的常量(数据在程序加载的时候,被加载到数据区),const只是编译器对在源代码的检查。实际上是伪常量(是辅助编写者,防止对一些不该改变的变量进行改变)

004013B0     PUSH EBP
004013B1     MOV EBP,ESP
004013B3     SUB ESP,10
004013B6     MOV DWORD PTR SS:[EBP-C],5
004013BD     LEA EAX,DWORD PTR SS:[EBP-C]
004013C0     MOV DWORD PTR SS:[EBP-4],EAX
004013C3     MOV DWORD PTR SS:[EBP-8],3
004013CA     LEAVE
004013CB     RETN

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值