问题
问下面两个程序的执行结果是什么?
unsigned short i; unsigned short index = 0; for(i = 0; i <index-1; i++){ printf("a\n"); }
unsigned short i; unsigned long index = 0; for(i = 0; i <index-1; i++){ printf("b\n"); }
我这里写说结论,原因后面分析。
结论:
第一个程序,一次都不循环。 第二个程序是一个死循环。原因是跳转语句 jge 和 jae之间的差异。
需要一点汇编的flag寄存器的知识。
分析程序一
先看程序一,我们把语句分开一下比较清晰:
unsigned short i;
unsigned short index = 0;
for(i = 0; i <index-1; i++) //这个地方会发生溢出
{
printf("a\n");
}
我们可以看到上面程序会发生溢出,当 index - 1 的时候溢出后,index的值会变成 65536(unsigned short的最大值),所以这个程序会一直死循环下去,但是并没有,我们来看一下汇编代码:
int a = 0;
00CB59FB mov dword ptr [ebp-60h],0
unsigned short i;
unsigned short index = 0;
00CB5A02 xor eax,eax
00CB5A04 mov word ptr [ebp-78h],ax
for(i = 0; i <index-1; i++)
00CB5A08 xor eax,eax
00CB5A0A mov word ptr [ebp-6Ch],ax
00CB5A0E jmp CCeshi4Dlg::OnInitDialog+15Ch (0CB5A1Ch)
00CB5A10 mov ax,word ptr [ebp-6Ch]
00CB5A14 add ax,1
00CB5A18 mov word ptr [ebp-6Ch],ax
00CB5A1C movzx eax,word ptr [ebp-6Ch]
00CB5A20 movzx ecx,word ptr [ebp-78h]
00CB5A24 sub ecx,1 //这里将 index -1 保存到 ecx中
00CB5A27 cmp eax,ecx //将index 于 i进行比较
00CB5A29 jge CCeshi4Dlg::OnInitDialog+184h (0CB5A44h) //最关键的跳转
{
printf("a\n");
00CB5A2B mov esi,esp
00CB5A2D push offset string "a\n" (0CDB294h)
00CB5A32 call dword ptr [__imp__printf (0CE3D3Ch)]
00CB5A38 add esp,4
00CB5A3B cmp esi,esp
00CB5A3D call @ILT+4275(__RTC_CheckEsp) (0CB30B8h)
}
00CB5A42 jmp CCeshi4Dlg::OnInitDialog+150h (0CB5A10h) //回去继续执行for循环
主要看 00CB5A29 处的 jge 语句,如果过这个 jge 成立进行跳转则for循环结束。
jge成立的条件:
转移条件:sf异或of == 0
转移说明:大于等于转移
其他说明:有符号数,两个标志位sf和of
当我们执行完 cmp 时, Flag寄存器值为:
EFL = 0x00000213 = 001000010011
因为 jge 是一个有符号的指令所以,cmp(00CB5A27)判断是是两个有符号数 所以 eax = 0,ecx的值之前因为是无符号数做的sub所以结果为0xFFFFffff(补码),因为计算机保存的都是补码,我们将0xFFFFffff当作有符号数来转换为0x1000…0001(-1),所以eax > ecx条件成立。
sf在第8位,of在第12位,所以 sf = 0,of = 0,条件成立(0 异或 0 == 0),所以 jge跳转程序结束。
SF,符号标志位。它记录相关指令执行后,其结果是否为负。如果结果为负,sf = 1;如果非负,sf = 0。
注意:当我们将数据当作无符号数来运算,SF的值则没有意义,但是指令还是会改变SF的值。
分析程序二
再看程序二,我们把语句分开一下比较清晰:
unsigned short i;
unsigned long index = 0;
for(i = 0; i <index-1; i++) //这个地方会发生溢出
{
printf("b\n");
}
我们可以看到上面程序会发生溢出,当 index - 1 的时候溢出后,index的值会变成 unsigned long的最大值,所以这个程序会一直死循环下去,我们来看一下汇编代码:
unsigned short i;
unsigned long index = 0;
00F55A02 mov dword ptr [ebp-78h],0
for(i = 0; i <index-1; i++)
00F55A09 xor eax,eax
00F55A0B mov word ptr [ebp-6Ch],ax
00F55A0F jmp CCeshi4Dlg::OnInitDialog+15Dh (0F55A1Dh)
00F55A11 mov ax,word ptr [ebp-6Ch]
00F55A15 add ax,1
00F55A19 mov word ptr [ebp-6Ch],ax
00F55A1D movzx eax,word ptr [ebp-6Ch]
00F55A21 mov ecx,dword ptr [ebp-78h]
00F55A24 sub ecx,1
00F55A27 cmp eax,ecx
00F55A29 jae CCeshi4Dlg::OnInitDialog+184h (0F55A44h) //最关键的跳转
{
printf("b\n");
00F55A2B mov esi,esp
00F55A2D push offset string "a\n" (0F7B294h)
00F55A32 call dword ptr [__imp__printf (0F83D3Ch)]
00F55A38 add esp,4
00F55A3B cmp esi,esp
00F55A3D call @ILT+4275(__RTC_CheckEsp) (0F530B8h)
}
00F55A42 jmp CCeshi4Dlg::OnInitDialog+151h (0F55A11h)
主要看 00F55A29 处的 jae 语句,如果过这个 jae 成立进行跳转则for循环结束。
jae成立的条件:
转移条件:cf == 0
转移说明:不低于,或者高于等于,或者进位标志转移清零时转移
其他说明:单个标志,无符号数
在转移指令之前有test、cmp等比较指令
当我们执行完 cmp 时, Flag寄存器值为:
EFL = 0x00000213 = 1000010011
cf在第1位,所以 cf = 1 条件不成立(cf != 0),所以 jge不会进行跳转程序进入死循环。
CF: 进位/借位标志,将两个操作数当做无符号数,在加减法指令下,若有进位或借位则CF为1,否则为0。
当执行cmp(00F55A27)指令时会进行减法操作从而改变cf的值。