将带符号整数除以2可编译为复杂的程序集输出,而不仅仅是移位

时间:2018-09-12 18:41:56

标签: assembly x86

考虑此C函数;

int triangle(int width, int height)
{
    return (width * height) / 2;
}

使用gcc(gcc -m32 -g -c test.c)编译时,会产生以下汇编(objdump -d -M intel -S test.o)。

test.o:     file format elf32-i386


Disassembly of section .text:

00000000 <triangle>:

int triangle(int width, int height)
{
   0:   55                      push   ebp
   1:   89 e5                   mov    ebp,esp
    return (width * height) / 2;
   3:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
   6:   0f af 45 0c             imul   eax,DWORD PTR [ebp+0xc]
   a:   89 c2                   mov    edx,eax
   c:   c1 ea 1f                shr    edx,0x1f
   f:   01 d0                   add    eax,edx
  11:   d1 f8                   sar    eax,1
  13:   5d                      pop    ebp
  14:   c3                      ret    

我已经知道,将整数n位右移会将其除以2 ^ n。但是,根据上述输出,有符号整数似乎被区别对待(这当然是有道理的)。如果我正确地读取了汇编输出,则在移位之前先将整数的符号位加进自己。

在右移之前将整数的符号位添加到自身的目的是什么?

1 个答案:

答案 0 :(得分:2)

对于负数,它将获得正确的“向零舍入”结果。通过将舍入运算移向负无穷大,可以使负数与C除法运算符的预期结果产生不同的结果。

一个例子是-1:向右移动1仍给出-1,但是C运算符/ 2给出0。

因此,额外的代码是对此效果的更正。如果不需要,请使用无符号或显式移位(但第二个选项的移植性较差)。