atoll谁都知道是字符串转long long。
但是日前发现字符串里的数字超过11位竟然就溢出了,好奇怪啊。
#include <stdio.h> int main(int argc,char**argv) { char bigNumStr[] = "12345678901"; long long llNum; llNum = atoll(bigNumStr); printf("%lld\n",llNum); return 0; }
看到这应该就有老鸟就笑了。没有#include 《stdlib.h》嘛。
但是为什么呢?
抠嗤了好一会,发现在没有stdlib.h的时候
编译器产生的汇编代码调用atoll的时候是这样的:
call atoll cltd movl %eax, 40(%esp) movl %edx, 44(%esp)
而在有stdlib.h的时候
编译器产生的汇编代码调用atoll的时候是这样的:
call atoll movl %eax, 40(%esp) movl %edx, 44(%esp)
这个cltd是神马东西呢?
这个cltd其实就是把32位数转换为64位数,这里就是一个比较常见的隐式转换。
但是返回值为long long的atoll为毛会返回一个32位数呢?
其实你在代码里随便调用一个不存在的函数,用-Wall选项编译就会发现如下提示:
atoll.c:6:2: warning: implicit declaration of function 'notExist' [-Wimplicit-function-declaration] printf("%lld %s\n",llNum,notExist(llNum)); ^ atoll.c:6:2: warning: format '%s' expects argument of type 'char *', but argument 3 has type 'int' [-Wformat=]
gcc把未定义的notExist的返回值自作主张的认定为int了。
so,答案就这样揭晓了。
应为调用atoll的时候没有包含它的声明文件stdlib.h。
所以在编我们的代码的时候,比如atoll.c,也就同样自认为返回值是int。
但是在链接程序的时候libc.so里是有atoll的实现的。如下:
[cz@localhost ~]$ nm /usr/lib/libc.so.6 | grep atoll 0002f8b0 T atoll
所以链接并没有出错。所以就造成了这个奇怪的问题。我们的代码里把atoll的返回值当成了int,赋值给long long类型,同时做隐式类型转换。cltd用eax的数值扩展,高位存到edx,所以之前的edx就被覆盖调了。
0 条评论。