编译器对unsigned数值运算进行的优化
昨天在讨论文档中的某个函数的实现方法的问题,我提议把某个循环计数变量unsigned int改成int,以便使用负值来表示出错信息。结果被人指出,改成int会影响代码效率,因为对这个变量有如下的操作:a = (a+1)%64,如果a为unsigned型,那么编译器会自动进行优化,而对于int型则不会。后来验证了一下果然如此。
测试环境为cygwin+gcc3.2。测试程序如下:
1 int main()
2 {
3 int a;
4 unsigned int b;
5 a = 20;
6 b = 21;
7 a = (a + 1) % 64;
8 b = (b + 1) % 64;
9 return 0;
0 }
编译并查看汇编代码:
$ gcc test.c
$ gdb a.exe # 调试执行
(gdb) b main # 在main函数处设置断点
(gdb) r # 运行
(gdb) disass # 反汇编
结果如下所示:
; a = 20
movl $0x14,0xfffffffc(%ebp)
; b = 21
movl $0x15,0xfffffff8(%ebp)
; a = a + 1
mov 0xfffffffc(%ebp),%edx
inc %edx
mov %edx,0xfffffff0(%ebp)
; a = a % 64
mov 0xfffffff0(%ebp),%eax
mov %eax,0xffffffec(%ebp)
cmpl $0x0,0xffffffec(%ebp)
jns 0x4010e3 <main+67>
addl $0x3f,0xffffffec(%ebp)
mov 0xffffffec(%ebp),%eax ; << here is <main+67>
sar $0x6,%eax
mov %eax,0xfffffffc(%ebp)
mov 0xfffffffc(%ebp),%eax
shl $0x6,%eax
mov 0xfffffff0(%ebp),%edx
sub %eax,%edx
mov %edx,%eax
mov %eax,0xfffffffc(%ebp)
; a = a + 1
mov 0xfffffff8(%ebp),%eax
inc %eax
; a = a % 64
and $0x3f,%eax
mov %eax,0xfffffff8(%ebp)
可以看出,对于无符号型整数,编译器会自动把 % 64的运算转换成 and 0x3f,而对于符号整数则不会。因此,在进行位运算或者是对2的幂取模时,应当尽量使用无符号整数,或者是在运算之前进行强制类型转换。