X86_64平台下strtoul诡异问题,都是#include惹的祸?

阅读更多
环境CentOS 5.4 X86_64 gcc version 4.1.2 20080704 (Red Hat 4.1.2-46)
碰到一个strtoul的诡异问题,简化出来就是这个程序
#include 

int main(){
  char *a = "4297757104";
  unsigned long int l = strtoul(a, NULL, 0);
  printf("%lu\n", l);
  return 0;
}

结果输出2789808而非4297757104。查了半天手册也没发现什么不对的,发现手册中有#include ,加上去居然就对了。百思不解,原来编译也能通过,为什么加个头文件就导致程序行为改变了?

之前也听说过GCC对某些函数会做内部特殊处理。既然程序行为改变,二进制级别上肯定就有区别。先用objdump -T long 将动态链接的函数导出一看,都一样。干脆直接objdum -S long> long.s 把程序反汇编,然后diff查看,结果发现没有加include的版本,多了cltq 这么条指令
  40050b:       e8 e8 fe ff ff          callq  4003f8
  400510:       48 98                   cltq
就是罪魁祸首了。这条指令是将eax寄存器的值符号扩展到rax。
4297757104的二进制是100000000001010101001000110110000,其中低32位截取下来是00000000001010101001000110110000,然后符号扩展到64位,只要将高32位补0即可。换算下来,刚好就是2789808。也就是说,这条指令把rax高32位的数据给清掉了。

教训:还是那两句老话
1)编译时一定要加上 -Wall,加上之后就会报一个 warning: implicit declaration of function 'strtoul'。否则,怎么死的都不知道。
2)不要忽略任何warning
不过,这个warning对于我等新手来说,很容易无视,也很难明白其中含义

你可能感兴趣的:(CentOS,GCC,C,C++,C#)