char * GetString(void)
{
char p[]="hello,world";
return p;
}
void main(void)
char *str=NULL;
str=GetString();}
现在问题就是:子函数的p数组在栈内存里,通过寄存器AX把这个栈地址返回了。
c 语言子函数返回的时候会保持栈平衡,所用的手法就是mov sp,bp,
通过debug调试发现,在该语句执行前,堆栈里的数据"hello,world"还在,
执行该语句后,堆栈该处的数据就变化了,变得不再是"hello,world",
所以想问下:堆栈平衡,仅仅是移动了sp指针,为什么会把原来栈里的数据清掉了呢?
又或者说是谁去做这样的事情呢?编译器,操作系统,还是CPU本身的机制呢?
我在WINDOWS的CMD窗口,和软盘启动的DOS都试过,结果一样。
试验了,确实如此。与调试器有关。试验这样的代码:
char static_ptr[20];
int i;
for(i=0;i<20;i++)static_ptr[ i ]=0;
str=GetString();
for(i=0;i<20;i++)static_ptr[ i ]=str[ i ];
printf("%s\n",str);
printf("%s\n",static_ptr);}
即在执行完GetString后直接将str中的值取出放入内存中,而不是作为局部指针存在。结果str打印出不确定字符串,static_ptr打印了hello,world.我们知道,局部指针指向的内存值在函数结束后是不确定的。
在非debug中,当函数执行到mov sp,bp的时候,实际上不会发生nanjingesc所叙述的情况(以上的例子证明了)。之所以在debug的环境中发生,是因为执行这条指令的时候,调试器本身也用到了堆栈,影响了这部分数据。同样的道理,为什么str打印出不确定的字符串,是因为printf影响了栈中的内容。这种现象不管用哪种调试器(当然是软件调试器,如TD,debug32等)应该都会发生。这个例子在林锐的书《高质量C/C++编程》中有叙述,但是他的描述不大准确,因为他的说法也是通过调试来发现数据变了,并没有说明这之间到底是怎么变化的。
我现在在想另外一个问题,如果使用JTAG来调试的话,会不会出现这样的问题。之前调试某款BIOS的时候遇到过,只要往sp中塞入一个值,如mov sp,200h(当然,我确定200h处是我自己规定的堆栈),200h处的数据立即就改变了,这个问题一直没有想明白,手上也没有类似的环境调试。张老师,看到的话能否帮助想想,谢谢^^
呵呵,二位都想得很深入。
这里的一个关键问题是CPU在发生异常时会自动向栈里压入包括IP寄存器在内的当前位置信息(参见《软件调试》11.1.3 P278)。而在调试时,单步跟踪会触发异常,导致CPU向栈里压入信息。
对于保护模式,因为异常处理代码在内核模式,所以要切换到使用内核态的栈,因此用户态的栈一般不会受影响。对于实模式情况,不涉及栈的切换,CPU会直接使用当前的栈,因此就会看到CPU曾经使用的痕迹。
对于现在的例子,编译成32位程序和编译成16位,结果应该是有所差异的。
对于32位的情况,应该是不会变的,除非有用户代码会用到这个空间,对于本例,在GetString返回后,即使在有调试器的情况下,观察main函数的str变量,还是应该可以看到本来的内容的,直到再调用其它函数,再使用栈,这个内容便可能会被破坏了。
仔细看下,如果还有问题,可以把编译好的exe发到我信箱一份。