此笔记参考《C++反汇编与逆向技术揭秘》
1.1 整数
C++提供的整数数据类型有三种:int、long、short。在Microsoft Visual C++6.0中,int和long占4字节,short占2字节。由于二进制数不方便显示和阅读,因此内存中的数据采用十六进制数显示。
1.1.1无符号整数(unsigned int)
无符号整数占4字节,取值范围:0x00000000~0xFFFFFFFF。由于是无符号整数,不存在正负之分,都是整数,故无符号整数在内存中以真值形式存放,即从内存取来参与运算的都是原码。
1.1.2 有符号整数
有符号整数4字节,有符号整数中,正数的表示区间为:0x00000000 ~ 0x7FFFFFFF;负数的表示区间为:0x80000000 ~ 0xFFFFFFFF。在计算机中,正数加负数的运算是用正数的原码加负数的补码得到结果的。正数的原码、反码、补码都一样,负数的反码是在原码的基础上,符号位不变(仍为1),数值位按位取反;负数的补码是在原码的基础上,符号位不变(仍为1),数值位按位取反,末位加1。
如何判断一段数据是有符号类型还是无符号类型呢?这就需要查看指令或者已知的函数如何操作次内存地址,根据操作方式或函数相关定义得出该地址的数据类型。如API调用MessageBoxA,它有4个参数,查看帮助得知,第4个参数为一个无符号整数,从而可分析出这个传入数值的类型。
1.2 浮点数类型
在C/C++中,用两种数据类型来保存浮点数:float(单精度)、double(双精度)。float占4字节,double占8字节。
整数类型是将十进制转换成二进制保存在内存中,以十六进制方式显示。浮点类型并不是将一个浮点小数直接转换成二进制数保存,而是将浮点小数转换成的二进制码重新编码,在进行存储。C/C++的浮点数是有符号的。
C/C++浮点数强制转换为整数时,不会四舍五入,而是舍弃小数,不会进位。
浮点数的操作不会用到通用寄存器,而会使用协处理器的浮点寄存器,专门对浮点数进行运算处理。Microsoft Visual C++6.0在使用浮点数前,需先对浮点寄存器进行初始化,然后才能正常运行。
1.2.1 浮点数的编码方式
浮点数编码转换采用的是IEEE规定的编码标准,float和double这两种数据类型的转换原理相同,但由于表示的范围不一样,编码方式有些区别。IEEE规定的浮点数编码会将一个浮点数转换为二进制数。以科学计数法划分,将浮点数拆分为3部分:符号、指数、尾数。
十进制数127可表示为二进制数01111111.IEEE编码方式规定,当指数域小于01111111时为一个负数,反之为正数,因此01111111为0。
double类型占8字节内存空间,最高位表示符号,指数占11位,剩余42位用于表示尾数。
在float中,指数位范围用8位表示,加127后用于判断指数符号。在double中,由于扩大了精度,因此指数范围使用11位正数表示,加1023后可用于指数符号判断。
1.2.2 基本的浮点数指令
浮点数操作是通过浮点寄存器来实现的,浮点寄存器是通过栈结构来实现的,由ST(0)~ST(7)共8个栈空间组成,每个浮点寄存器占8字节。每次使用浮点寄存器都是率先使用ST(0),而不能越过ST(0)直接使用ST(1)。浮点寄存器的使用就是压栈、出栈的过程。当ST(0)存在数据时,执行压栈操作后,ST(0)中的数据将装入ST(1)中,如无出栈操作,将顺序的向下压栈,直到浮点寄存器占满。常用浮点数指令的介绍如下表2-1所示,IN表示操作数入栈,OUT表示操作数出栈。
使用浮点指令时,都要先利用ST(0)进行运算。当ST(0)中有值时,便会将ST(0)中的数据顺序向下存放到ST(1)中,然后再将数据放入ST(0)中。如果再次操作ST(0),则会先将ST(1)中的数据放入ST(2)中,然后将ST(0)中的数据放入ST(1)中,最后才将新的数据存放到ST(0)。以此类推在8个浮点寄存器都有值的情况下继续向ST(0)中存放数据,这时会丢弃ST(7)中数据信息。
1.3 字符和字符串
在C++中,字符的编码格式分两种:ASCII和Unicode。Unicode是ASCII的升级编码格式。在Microsoft Visual++ 6.0中,使用char定义ASCII编码格式的字符,使用wchar_t定义Unicode字符,wchar_t保存ASCII编码,不足位补0。ASCII编码在内存中占一个字节,Unicode占2个字节。
定义字符串的时候都会指定好首地址,结束地址的确定有两种做法,一种是在首地址的四字节中保存字符串长度;另一种是在字符串的结尾处使用一个结束符。C++使用‘\0’作为结束符。ASCII使用一个字节‘\0’,Unicode使用两个字节‘\0’。不能使用处理ASCII编码的函数对Unicode编码进行处理。
1.4 布尔类型
布尔类型在内存中占1字节;布尔类型只比较两个结果值:真、假,C++中定义0为假,非0为真。
1.5 地址、指针和引用
地址:在C++中,地址标号使用十六进制表示。取一个变量的地址使用“&”,只有变量才存在内存地址,常量没有地址(不包括const定义的伪常量)。
i = 100 printf("%x",&i);是正确的,printf("%x",&100);是错误的,用cout输出两者也一样。
指针(TYPE *):任何数据类型都可以定义指针,指针本身也可以是一种数据类型,用于保存各种数据类型在内存中的地址,指针变量同样可以取出地址,所以会出现多级指针。
引用(TYPE &):引用在C++中不可以单独定义,并且在定义时就要进行初始化。引用表示一个变量的别名,对它的任何操作,本质上都是在操作它所表示的变量。
1.5.1 指针和地址的区别
指针和地址的不同点:
指针 | 地址 |
---|---|
变量,保存变量地址 | 常量,内存标号 |
可修改,再次保存其他变量地址 | 不可修改 |
可以对其执行取地址操作得到地址 | 不可执行取地址操作 |
包含对保存地址的解释信息 | 仅仅有地址无法解释数据 |
指针和地址的相同点:
指针 | 地址 |
---|---|
取出指向地址内存中的数据 | 取出地址对应内存中的数据 |
求两个地址的差 | 求两个地址的差 |
指针只支持加法和减法运算,例如。int型的指针加一,地址会加4.两指针的加法没有意义。
1.5.3 引用
引用类型在C++中被描述为变量的别名。实际上,C++为了简化指针操作,对指针的操作进行了封装,产生了引用类型。实际上引用类型就是指针类型,只不过它用于存放地址的内存空间对使用者而言是隐藏的。