昨天在讨论文档中的某个函数的实现方法的问题,我提议把某个循环计数变量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的幂取模时,应当尽量使用无符号整数,或者是在运算之前进行强制类型转换。