今天在做题的时候看到了这样一个题
char* getmemory(void)
{
char p[] = "hellow beijing";
return p;
}
void test(void)
{
char* str = NULL;
str = getmemory();
printf(str);
}
int main()
{
test();
return 0;
}
该题运行的结果是:烫烫烫烫烫烫烫烫,我刚开始看见这个代码的时候觉得没有问题啊,但为什么打印出来的数据是这样,后来我在网上查找了一番,原来该题属于返回栈区空间地址的问题,那什么是返回栈区空间地址的问题。下面我们就来研究分析一下
先举一个简单的例子
int test(void)
{
int a = 10;
return a;
}
int main()
{
int p = test();
printf("%d\n",p);
return 0;
}
我们在写这个程序的时候,运行出的结果为10。这里的变量a在内存中开辟了一块内存空间,该空间里面存放的是10,同时该值也被保存在了寄存器里面,当函数执行完毕,变量a开辟的空间就被回收了,但寄存器里面的值还在,函数调用返回的值就是寄存器里面的10,所以打印出来就为10.这里是返回变量本身,不是返回栈区上的地址。再来看下一个例子
int* test(void)
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
printf("%d\n", *p);
return 0;
}
这里返回的值也是10,但是要说明的是,该代码是错误代码,上面的代码属于典型的返回栈空间地址问题,我们在稍微添加一下代码,它的值就不再为10了,请看下面。
int* test(void)
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
printf("hehe\n");
printf("%d\n", *p);
return 0;
}
现在的结果就是:hehe 5
我们在分析一下:变量a在内存中开辟一块内存区域,该区域用来存放10,有人会问寄存器里面存放的是什么,这里理论上有两个寄存器,第一个寄存器里面存放的是a变量的值,两一个寄存器里面存放的是a变量的地址,因为这里返回的是a变量的地址,所以返回寄存器里面存放的是a变量的地址。p指针用来接收函数返回来的地址,在没有第一个printf函数时,对p解引用拿到的是寄存器里面的值,也就是10.而为什么多了一个printf以后p的值就发生了改变啦。在这里就要涉及到内存中存放数据时压栈的问题了,下面看图
我们知道每一次在调用函数的时候,都会在栈区上面开辟内存空间,进去main函数,现在栈区上面为main函数开辟一块内存区域,然后将指针变量放在该区域内,在调用test函数时,再在main函数上面开辟一块内存区域,将a的值放在里面,因为返回的是一个地址,指针变量p接收到该地址,并将地址保存下来。当执行万test函数时,栈区上面的空间就回收了,当没有printf("hehe")时,指针p凭着地址找回去可能能找到里面的内存里面的值 ,这种情况仅限于该内存没有被占用。而当有printf("hehe")时,调用printf函数时,栈区上面为printf函数开辟了一块内存区域,里面放了hehe,放的内容就将之前test函数在内存中的类容给覆盖了,所以这是的p指针拿着地址找过去的时候,打印出来的当然就不再是10了。
所以回到之前那个题,虽然内存为字符数组p开辟了一块内存空间,但是p数组出了这个函数,这块空间就被操作系统回收了,而返回的又是p数组名,我们知道数组名就是地址常量,所以返回的是栈区上面的地址。而p数组里面的内容,当出了函数以后,就被回收了,str拿到的是一个没有指向任何空间的指针,也就是野指针。所以在打印str时,打印出来的就是乱码。
好了上面就是关于返回栈区空间地址的问题。