这是2012年9月24日google的笔试题目(大意):
#include <stdio.h> char* fun() { char X[100]; sprintf(X, "Hello World!"); return X+6; } int main() { printf("%s", fun()); return 0; }
对于上面的程序,会不会崩溃,如果不会崩溃,那么会输出什么? 【可选项大致有1)一定会崩溃 2)可能会崩溃,输出World 3)、、、忘了】
对这道题目我进行分析下:
1.首先,fun返回的是一个指针,即fun函数局部变量X的地址+6,而main函数中是要以字符串形式输出X+6开始地址上的数据;
2.我们知道X[100]是分配在栈上的空间,在函数返回时,局部数据将会被”清除“;由于栈上空间的分配是线性分配,具体来说就是从高地址向低地址分配,ESP指向栈顶,即栈空间的分配和释放是通过减小或增大ESP的值来实现。那么在释放局部数据X[100]时,实际上只是简单对ESP实施了加100的偏移实现,而对数据并未进行任何操作,即原来数据仍然在栈上,只不过不在EBP和ESP之内。这也就是为什么前面的“清除”要加引号的缘故。因此即使函数返回,但访问X+6仍然可以指向字符串“World”,是不是因此最后输出的就是World呢?实际测试,不是!!
3. 在函数返回后,访问X+6,实际上该地址是栈上的地址,但并非介于ebp和esp内,并且处于esp的低地址方向,即该地址可以再次被分配利用。
4. printf("%s",X+6)时,X+6仍然指向World。那么输出为什么不是World呢?通过跟踪printf发现,由于printf函数需要大量的栈空间,所以X+6指向的地方被再次利用,原来的数据被覆盖了!!所以输出不是World(注:Win下测试,输出空;Mac下测试输出sionq)。那么输出什么呢?不确定,取决于printf函数的实现。在考场上可能没时间调试,故不确定输出什么。
5. 关于崩溃,姑且认为是不可能的。 因为x是个有效地址,所以X+6必然也是个有效的地址,故不存访问地址出错,故不崩溃。
总结一下:首先,输出是不确定的,取决于printf函数内部对栈空间的使用,再者程序一定不会崩溃。