数据类型:
C/C++语言中,数据类型有很多,常用的:int、long、short、char以及他们的unsigned型。我们熟知,int、long都是占用32位(4字节)。short占16位(2字节)。int和long的范围为0x00000000~0x7FFFFFFF、0x80000000~0xFFFFFFFF。对应的unsigned型范围为:0x00000000~0xFFFFFFFF。而short也类似,都是基础知识,这里不做介绍。
在内存中,数字都是二进制,为了方便,通常都是按十六进制显示的,例如int中的100,实际上是0x64,然而,数据本身是4字节,其他的应该填充0,所以实际上是0x00000064。long也一样,short也类似,下面,我们回来验证。
汇编下的数据类型:
分析程序如下:
在内存窗口中,我们输入&n_i,查看n_i的内存处,可以看见如下:
可以发现,里面全是CCCCCCCC,这是由于VS工具在没有使用的位置全部初始化成CCCCCCCC,方便我们调试,然后,F10,执行一步程序,可以发现,上图黄色区域的四个字节变成了(64 00 00 00),并不是(00 00 00 64),同样的,我们在将内存窗口定位到u_i变量的内存位置,执行程序到初始化u_i,可发现,还是一样的效果,实际上,Windows存储数据本来就是按照这样,低位对应内存低位,高位对应内存高位。实际上这被称为小尾方式存储。对应的也有大尾方式,只是存储方式的不同,但我们任然可以理解,实际上,存储的就是0x00000064。
补码:计算机中,负数存储的是补码,不嘛的计算等是计算机基础知识,这里不做介绍,但是,这对反汇编很重要。
然而,这里存在一点比较让人疑惑,在汇编中,有符号和无符号的int都是占用32位,例如-2,即0xFFFFFFFE,按照有符号解析,就是-2,按照无符号解析,就是4294967294,结果完全不一样。我们在看反汇编的时候,同样是32位,如果没区分好有符号无符号,结果就可能大不一样,因此,在分析反汇编程序的时候,需要通过其他的方式区分有无符号。
在汇编中,有时候,我们还需要保存浮点数,C/C++中,用float和double保存浮点数,区别在于占用字节不一样,double占用8字节float占用四字节,double精度更高。两者都是使用的IEEE编码。
IEEE编码:
IEEE编码以科学计数法将浮点数的二进制分为符号位,指数,尾数三部分,以float为例,首位为符号位,之后8位为指数为,后面23位均为尾数部分
例如使用float保存12.25ff:12的二进制为1100,0.25的二进制为.01(8 4 2 1 . 1/2 1/4 1/8),因此,12.25保存为二进制为1100.01,即1.10001,指数为3,是一个正数,符号位为0,指数为3,需要加127,=130,放在指数位为10000010,在IEEE编码中,首位恒定为1(1.10001中第一个1),因此可以不记录,可以直接将10001保存在尾数部分,尾数不足时,全部补0。
double的数据存储也是大同小异,对于浮点数,特别注意的是其转换过程,对于CPU中,也有专门的浮点数寄存器。
程序分析:
对指针和数组的分析:
可以看出,第一个就是指针的赋值,说到指针大家可能更难理解,实际上,这一步就是将0x011ECD1C这个数赋值到一个指针中(char*),这个指针虽然是char*,但是char*是指针,也是占4字节,只有char是占1字节。
下一条C++代码:char arr_str[] = "I Love Mark";被翻译成了很长的代码:如下:
看起来,被翻译成了很多行,然而,实际上做的事情很简单,就是将“I Love Mark”按照4字节的单位搬到arr_str中,我们可以发现,实际上,代码是有规律的,分三次,每次将四字节搬到数据寄存器eax,然后,将eax搬到对应的内存地址,这里内存地址也是每次移动四个。搬到的地方为栈区,之前我们就已经知道了ebp为栈顶。
对指针和引用的分析:
上面代码,我们可以看出,指针和引用的汇编代码完全一样,都是使用lea指令取出地址,再将地址赋给变量。因为,引用属于编译时期检查,对于最终的运行没有任何影响,他只作用于编译器,在编译的时候的一些处理。在反汇编时,我们看不出是使用的引用还是指针,但是,他们最终效果都一样,这并不影响我们的反汇编过程。
引用并不会影响程序的执行速度,而且还比指针多了编译器的一些检测,所以,在C++编程的时候,使用引用更好。
对宏定义和const的分析
我们可以看到,宏定义本身不会产生汇编代码,而在使用的时候,直接就替换了,直接使用的64h,而const却和普通的赋值是一样的,在汇编下,加const和不加const,本质一样,这也从本质上说明了,const只是作用于编译器,是编译期间使用的。