关于printf的思考
在《程序员面试宝典》上有这么一道题:
以下代码输出结果是什么?
#include <stdio.h>
main()
{
int arr[] = {6, 7, 8, 9, 10};
int *ptr = arr;
*(ptr++) += 123;
printf("%d, %d\n", *ptr, *(++ptr));
}
A. 8, 8 B. 130, 8 C. 7, 7 D. 7, 8
这道题出现了我在这篇《漫谈全局变量》一样的问题,不同的环境下得到的结果不同。尽管又是这种万恶的问题,但是通过这道题,我还是学到很多东西。
首先你要知道,书中给出的答案是A。我想问题主要有二: *(ptr++) += 123
怎么解?printf("%d, %d\n", *ptr, *(++ptr))
又怎么解?
先说说这个万恶的“++”,这东西简直是一种灾难,不知道多少的bug是由它引入的。对于*(ptr++)
,其实加不加括号是一样的,它都先寻址,在进行“++”操作。相当于,取*ptr
后,再ptr++
。相反,如果使用*(++ptr)
,则是先“++”,在寻址,结果一定是不同的。
那么*(ptr++) += 123
按照书中的解释,应该是:
*ptr = *ptr + 123; ptr++;
怎么想这都是奇葩的用法,为什么不是:
*(ptr++) = *(ptr++) + 123;
我猜测大概是编译器采取压栈的方式,先压入123,再处理“+=”,再压入*(ptr++)
,这里压入的值即是*ptr
,再把最后的处理“++”。
那么再来看printf
,在Mac下Clang编译器,得到的答案是7,8,而在Ubuntu下Gcc编译器,得到的是8,8。根据书中的解释,printf
是从右侧开始压栈,先处理*(++ptr)
,此时已经指向了第三个数8(注意“++”的位置)。再处理*ptr
使,自然是8。Clang下之所以会出现不同的结果,我猜测可能由于压栈顺序是由左至右。
没准你可以尝试一下将print
那句改为:
printf("%d, %d\n", *ptr, *(++ptr));
得到的结果又将不同,这里其实就和上面的*(ptr++) += 123
是一个道理。
#include <stdio.h>
int main(int argc, char const *argv[])
{
int b = 3;
printf("%d, %d\n", b, b++);
return 0;
}
上面这个例子更简单明了。不同的环境下得到的答案同样不一样。b++
和++b
也不一样。就算将上面代码反汇编,你也会发现printf的汇编代码很复杂,据说linux下printf
的源代码竟有16页之多。。。
抛去这种万恶的面试题,我想好的编程习惯非常重要,尤其是在处理“++”这个问题上,尽量不要使用复杂的用法。
blog comments powered by Disqus