18 February 2014

在《程序员面试宝典》上有这么一道题:

以下代码输出结果是什么?

#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