- 浮点数类型
IEEE标准从逻辑上采用一个三元组{S, E, M}来表示一个数N,它规定基数为2,符号位S用0和1分别表示正和负,尾数M用原码表示,阶码E用移码表示。根据浮点数的规格化方法,尾数域的最高有效位总是1,由此,该标准约定这一位不予存储,而是认为隐藏在小数点的左边,因此,尾数域所表示的值是1.M(实际存储的是M),这样可使尾数的表示范围比实际存储多一位。为了表示指数的正负,阶码E通常采用移码方式来表示,将数据的指数e 加上一个固定的偏移量后作为该数的阶码,这样做既可避免出现正负指数,又可保持数据的原有大小顺序,便于进行比较操作。IEEE 754规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80位实现)。只有32位模式有强制要求,其他都是选择性的。大部分编程语言都有提供IEEE浮点数格式与算术,但有些将其列为非必需的。例如,IEEE 754问世之前就有的C语言,有包括IEEE算术,但不算作强制要求(C语言的float通常是指IEEE单精确度,而double是指双精确度)。整体呈现二进制浮点数是以符号数值表示法的格式存储——最高有效位被指定为符号位(sign bit);“指数部分”,即次高有效的e个比特,存储指数部分;最后剩下的f个低有效位的比特,存储“有效数”(significand)的小数部分(在非规约形式下整数部分默认为0,其他情况下一律默认为1)。指数偏移值指数偏移值(exponent bias),是指浮点数表示法中的指数域的编码值为指数的实际值加上某个固定的值,IEEE 754标准规定该固定值为 ,其中的为存储指数的比特的长度。以单精度浮点数为例,它的指数域是8个比特,固定偏移值是。此为有号数的表示方式,单精度浮点数的指数部分实际取值是从-127到128。例如指数实际值为,在单精度浮点数中的指数域编码值为,即。采用指数的实际值加上固定的偏移值的办法表示浮点数的指数,好处是可以用长度为 e个比特的无符号整数来表示所有的指数取值,这使得两个浮点数的指数大小的比较更为容易,实际上可以按照字典序比较两个浮点表示的大小。这种移码表示的指数部分,中文称作阶码。
正单浮点数:
25: float fFloat0 = 12.25f; 0130723E F3 0F 10 05 CC 2E 3A 01 movss xmm0,dword ptr [__real@41440000 (013A2ECCh)] 01307246 F3 0F 11 45 F8 movss dword ptr [fFloat0],xmm0
负单浮点数:
无穷转换二进制:
双浮点数:
浮点数使用:
54: // 浮点数使用 55: float fFloat = (float)argc; 01307272 F3 0F 2A 45 08 cvtsi2ss xmm0,dword ptr [argc] 01307277 F3 0F 11 45 C4 movss dword ptr [fFloat],xmm0 //movss 保存单浮点数 56: printf("%f", fFloat);//单浮点数作为参数时,需要先转换为双浮点数。 0130727C F3 0F 5A 45 C4 cvtss2sd xmm0,dword ptr [fFloat] //cvtsi2ss 单浮点数-->双浮点数 01307281 83 EC 08 sub esp,8 //开辟8字节,以保存转换结果 01307284 F2 0F 11 04 24 movsd mmword ptr [esp],xmm0 //movsd 保存双浮点数 01307289 68 50 2E 3A 01 push offset string "%f" (013A2E50h) 0130728E E8 32 A1 FF FF call _printf (013013C5h) 01307293 83 C4 0C add esp,0Ch 57: argc = (int)fFloat; 01307296 F3 0F 2C 45 C4 cvttss2si eax,dword ptr [fFloat] //cvttss2si 单浮点数-->整形 57: argc = (int)fFloat; 0130729B 89 45 08 mov dword ptr [argc],eax 58: printf("%d", argc); 0130729E 8B 45 08 mov eax,dword ptr [argc] 013072A1 50 push eax 013072A2 68 54 2E 3A 01 push offset string "%d" (013A2E54h) 013072A7 E8 19 A1 FF FF call _printf (013013C5h) 013072AC 83 C4 08 add esp,8 59: 60: fFloat = GetFloat(); 013072AF E8 77 C2 FF FF call GetFloat (0130352Bh) 013072B4 D9 5D C4 fstp dword ptr [fFloat] //ST(0)出栈,存入[fFloat] 61: printf("%f", fFloat); 013072B7 F3 0F 5A 45 C4 cvtss2sd xmm0,dword ptr [fFloat] //单浮点数作为参数,转换为双浮点数 013072BC 83 EC 08 sub esp,8 013072BF F2 0F 11 04 24 movsd mmword ptr [esp],xmm0 61: printf("%f", fFloat); 013072C4 68 50 2E 3A 01 push offset string "%f" (013A2E50h) 013072C9 E8 F7 A0 FF FF call _printf (013013C5h) 013072CE 83 C4 0C add esp,0Ch
GetFloat():
6: float GetFloat() 7: { 01307130 55 push ebp 01307131 8B EC mov ebp,esp 01307133 81 EC C0 00 00 00 sub esp,0C0h 01307139 53 push ebx 0130713A 56 push esi 0130713B 57 push edi 0130713C 8D BD 40 FF FF FF lea edi,[ebp-0C0h] 01307142 B9 30 00 00 00 mov ecx,30h 1: // DataType.cpp : Defines the entry point for the console application. 2: // 3: 4: #include "stdafx.h" 5: #include <stdio.h> 6: float GetFloat() 7: { 01307147 B8 CC CC CC CC mov eax,0CCCCCCCCh 0130714C F3 AB rep stos dword ptr es:[edi] 8: return 12.25f; 0130714E D9 05 CC 2E 3A 01 fld dword ptr [__real@41440000 (013A2ECCh)] //关键就这一句,将浮点数存入ST(0) 9: } 01307154 5F pop edi 01307155 5E pop esi 01307156 5B pop ebx 01307157 8B E5 mov esp,ebp 01307159 5D pop ebp 0130715A C3 ret
- 字符与字符串
- 布尔类型
71: // 布尔类型 72: bool bBool; 73: if (argc > 0) 013072ED 83 7D 08 00 cmp dword ptr [argc],0 013072F1 7E 06 jle main+0D9h (013072F9h) 74: { 75: bBool = true; 013072F3 C6 45 A3 01 mov byte ptr [bBool],1 //bool类型占用1字节,true 1;false 0; 76: } 77: else 013072F7 EB 04 jmp main+0DDh (013072FDh) 78: { 79: bBool = false; 013072F9 C6 45 A3 00 mov byte ptr [bBool],0 80: } 81: 82: 83: if (bBool == false) 013072FD 0F B6 45 A3 movzx eax,byte ptr [bBool] 80: } 81: 82: 83: if (bBool == false) 01307301 85 C0 test eax,eax 01307303 75 0D jne main+0F2h (01307312h) 84: { 85: printf("布尔类型 "); 01307305 68 A0 2E 3A 01 push offset string "xb2xbcxb6xfbxc0xe0xd0xcd " (013A2EA0h) 0130730A E8 B6 A0 FF FF call _printf (013013C5h) 0130730F 83 C4 04 add esp,4 86: }
- 地址、指针和引用
对指针取内容:
89: 90: // 对指针取内容 91: int nVar = 0x12345678; 00E6723E C7 45 F8 78 56 34 12 mov dword ptr [nVar],12345678h 92: int *pnVar = &nVar; 00E67245 8D 45 F8 lea eax,[nVar] //引用-- 取地址 00E67248 89 45 EC mov dword ptr [pnVar],eax //指针变量保存地址 93: char *pcVar = (char*)&nVar; 00E6724B 8D 45 F8 lea eax,[nVar] 00E6724E 89 45 E0 mov dword ptr [pcVar],eax 94: short *psnVar = (short*)&nVar; 00E67251 8D 45 F8 lea eax,[nVar] 00E67254 89 45 D4 mov dword ptr [psnVar],eax 95: printf("%08x ", *pnVar); 00E67257 8B 45 EC mov eax,dword ptr [pnVar] 00E6725A 8B 08 mov ecx,dword ptr [eax] //*pnVar 取4字节类型变量 00E6725C 51 push ecx 00E6725D 68 50 2E F0 00 push offset string "%08x " (0F02E50h) 00E67262 E8 5E A1 FF FF call _printf (0E613C5h) 00E67267 83 C4 08 add esp,8 96: printf("%08x ", *pcVar); 00E6726A 8B 45 E0 mov eax,dword ptr [pcVar] 00E6726D 0F BE 08 movsx ecx,byte ptr [eax] //1字节类型变量 00E67270 51 push ecx 00E67271 68 50 2E F0 00 push offset string "%08x " (0F02E50h) 00E67276 E8 4A A1 FF FF call _printf (0E613C5h) 00E6727B 83 C4 08 add esp,8 97: printf("%08x ", *psnVar); 00E6727E 8B 45 D4 mov eax,dword ptr [psnVar] 00E67281 0F BF 08 movsx ecx,word ptr [eax] //2字节类型变量 00E67284 51 push ecx 00E67285 68 50 2E F0 00 push offset string "%08x " (0F02E50h) 00E6728A E8 36 A1 FF FF call _printf (0E613C5h) 00E6728F 83 C4 08 add esp,8
指针偏移:
//指针偏移 100: char cVar[5] = {0x01, 0x23, 0x45, 0x67, 0x89}; 00217238 C6 45 F0 01 mov byte ptr [cVar],1 0021723C C6 45 F1 23 mov byte ptr [ebp-0Fh],23h 00217240 C6 45 F2 45 mov byte ptr [ebp-0Eh],45h 00217244 C6 45 F3 67 mov byte ptr [ebp-0Dh],67h 00217248 C6 45 F4 89 mov byte ptr [ebp-0Ch],89h 101: 102: int *pnVar = (int*)cVar; 0021724C 8D 45 F0 lea eax,[cVar] 0021724F 89 45 E4 mov dword ptr [pnVar],eax 103: char *pcVar = (char*)cVar; 00217252 8D 45 F0 lea eax,[cVar] 00217255 89 45 D8 mov dword ptr [pcVar],eax 104: short *psnVar = (short*)cVar; 00217258 8D 45 F0 lea eax,[cVar] 104: short *psnVar = (short*)cVar; 0021725B 89 45 CC mov dword ptr [psnVar],eax 105: //x86 下指针变量都占4字节 106: pnVar += 1; 0021725E 8B 45 E4 mov eax,dword ptr [pnVar] 00217261 83 C0 04 add eax,4 //int指针指向的变量类型大小为4字节 00217264 89 45 E4 mov dword ptr [pnVar],eax //相当于指向cVar[4], 107: pcVar += 1; 00217267 8B 45 D8 mov eax,dword ptr [pcVar] 0021726A 83 C0 01 add eax,1 //char指针指向的变量类型大小为1字节 0021726D 89 45 D8 mov dword ptr [pcVar],eax //指向元素cVar[1] 108: psnVar += 1; 00217270 8B 45 CC mov eax,dword ptr [psnVar] 00217273 83 C0 02 add eax,2 00217276 89 45 CC mov dword ptr [psnVar],eax //指向元素cVar[2] 109: 110: printf("%08x ", *pnVar); 00217279 8B 45 E4 mov eax,dword ptr [pnVar] 109: 110: printf("%08x ", *pnVar); 0021727C 8B 08 mov ecx,dword ptr [eax] 0021727E 51 push ecx 0021727F 68 50 2E 2B 00 push offset string "%08x " (02B2E50h) 00217284 E8 3C A1 FF FF call _printf (02113C5h) 00217289 83 C4 08 add esp,8 111: printf("%08x ", *pcVar); 0021728C 8B 45 D8 mov eax,dword ptr [pcVar] 0021728F 0F BE 08 movsx ecx,byte ptr [eax] 00217292 51 push ecx 00217293 68 50 2E 2B 00 push offset string "%08x " (02B2E50h) 00217298 E8 28 A1 FF FF call _printf (02113C5h) 0021729D 83 C4 08 add esp,8 112: printf("%08x ", *psnVar); 002172A0 8B 45 CC mov eax,dword ptr [psnVar] 002172A3 0F BF 08 movsx ecx,word ptr [eax] 002172A6 51 push ecx 002172A7 68 50 2E 2B 00 push offset string "%08x " (02B2E50h) 002172AC E8 14 A1 FF FF call _printf (02113C5h) 002172B1 83 C4 08 add esp,8
引用类型:
//引用类型 115: int nVar = 0x12345678; 0086717E C7 45 F8 78 56 34 12 mov dword ptr [nVar],12345678h 116: //引用类型定义 117: int &nVarTpye = nVar; 00867185 8D 45 F8 lea eax,[nVar] 00867188 89 45 EC mov dword ptr [nVarTpye],eax 118: //调用函数,参数为引用类型 119: Add(nVar); 0086718B 8D 45 F8 lea eax,[nVar] 0086718E 50 push eax 0086718F E8 AF A6 FF FF call Add (0861843h) 00867194 83 C4 04 add esp,4
add():
11: void Add(int &nVar) 12: { 008670E0 55 push ebp 008670E1 8B EC mov ebp,esp 008670E3 81 EC C0 00 00 00 sub esp,0C0h 008670E9 53 push ebx 008670EA 56 push esi 008670EB 57 push edi 008670EC 8D BD 40 FF FF FF lea edi,[ebp-0C0h] 008670F2 B9 30 00 00 00 mov ecx,30h 008670F7 B8 CC CC CC CC mov eax,0CCCCCCCCh 008670FC F3 AB rep stos dword ptr es:[edi] 13: nVar++; 008670FE 8B 45 08 mov eax,dword ptr [nVar] 00867101 8B 08 mov ecx,dword ptr [eax] 00867103 83 C1 01 add ecx,1 00867106 8B 55 08 mov edx,dword ptr [nVar] 00867109 89 0A mov dword ptr [edx],ecx 14: } 0086710B 5F pop edi 0086710C 5E pop esi 0086710D 5B pop ebx 0086710E 8B E5 mov esp,ebp 00867110 5D pop ebp 00867111 C3 ret
- 常量
//const常量修改 132: 133: const int nConst = 5; 00867197 C7 45 E0 05 00 00 00 mov dword ptr [nConst],5 134: int *pConst = (int*)&nConst; 0086719E 8D 45 E0 lea eax,[nConst] 008671A1 89 45 D4 mov dword ptr [pConst],eax 135: *pConst = 6; 008671A4 8B 45 D4 mov eax,dword ptr [pConst] 008671A7 C7 00 06 00 00 00 mov dword ptr [eax],6 136: int nVar1 = nConst; 008671AD C7 45 C8 05 00 00 00 mov dword ptr [nVar1],5 //由于const修饰的变量nConst被赋值一个数字常量5,
//编译器在编译过程中发现nConst的初值是可知的,并且被修饰为const。之后所有使用nConst的地方都以这个可预知值替换,
//故int nVar=nConst;对应的汇编代码没有将nConst赋值给nVar,而是用常量值5代替。如果nConst的值为一个未知值,那么编译器将不会做此优化。
#define是一个真常量,而const却是由编译器判断实现的常量,是一个假常量。在实际中,使用const定义的变量,最终还是一个变量,只是在编译器内进行了检查,发现有修改则报错。
由于编译器在编译期间对const变量进行检查,因此被const修饰过的变量是可以修改的。利用指针获取到const修饰过的变量地址,强制将指针的const修饰去掉,就可以修改对应的数据内容