详解C语言中Return返回函数局部变量的问题

在计算机中,释放空间并不需要将空间中的内容全部置成0或者1,而是只要设置这一块空间的数据无效即可。比如在下载文件时需要花很长时间,但是删除文件却只要几秒钟,这是因为操作系统只是把文件标识(文件头链接)删掉了,文件原文还保留着,我们没了文件标识就找不到这个文件了。所以删除后的文件,还可以用特殊的办法被找回来。

这也就意味着,当函数结束调用的时候,函数中的局部变量实际上还是在的,只是函数原来的空间还给编译器(释放)了,也就是说函数中的局部变量是可以被编译器修改的。

虽然函数结束后空间还给了编译器,但是我们依然可以通过指针找到对应的局部变量的空间。


return返回栈区局部变量的指针

通过上面的分析,如果我们返回局部变量的指针,是不是可以直接找到这个局部变量呢?

返回了一个局部变量的指针,而恰好局部变量偏偏又在函数结束后销毁,但指针并没有被销毁,而是被返回,那也就是说,指针指向的正是一个被销毁了的对象。

比如下面的代码:

#include <stdio.h>
char* returnStr()
{
    char p[] = "hello world!";
    return p;
}
int main()
{
    char* str;
    str = returnStr();
    printf("%s\n", str);
    return 0;
}

BA28D4D2-E0B2-A76A-641B-2573BDBE7A16.png

可以看到原来p的空间已经被修改了,但是很奇怪,是谁进行的修改呢?

通过调试可以发现,当函数结束后,运行printf打印之前,str指向的空间中的字符串是在的:

501C7A43-AA06-E0EA-E582-AB7EE2213CD4.png

但是一运行printf打印操作,str指向的空间中的字符串就会被修改:

776E8C49-03C8-5671-2473-B3C338800730.png

这其实很好解释,因为printf本身也是一个函数,函数都是在栈区开辟的,而函数开辟的空间叫做栈帧,函数结束栈帧就还给了编译器:

52B37ED6-6757-5C20-5B27-3D6CE12B6E9D.png

了解了这些,那是不是只要我们多建立几个函数栈帧,是不是就可以使returnStr的函数栈帧不被覆盖了?答案是肯定的:

#include <stdio.h>
char* fun6()
{
    char p[] = "hello world";
    return p;
}
char* fun5()
{
    return fun6();
}
char* fun4()
{
    return fun5();
}
char* fun3()
{
    return fun4();
}
char* fun2()
{
    return fun3();
}
char* fun1()
{
    return fun2();
}
char* fun()
{
    return fun1();
}
int main()
{
    char* str;
    str = fun();
    printf("%s\n", str);
    return 0;
}

AA00B147-520A-4CEA-1FE1-C76ED42C0F38.png

97BFE54F-7B90-F7F1-3F41-D9CDDC6D227B.png

不过随着函数栈帧的逐渐增多,原来的fun6函数空间迟早也会被覆盖。

在函数体内定义的局部变量是有临时性的,当局部变量释放后随时都有可能会被修改,所以我们不能通过指针使用已经被释放的局部变量。

return返回栈区局部的临时变量

如果我们不返回指针,而是返回局部变量会怎么样呢?

按理来说局部变量也会被修改。

#include<stdio.h>
int test()
{
	int a = 10;
	return a;
}
int main()
{
	int b = test();
	printf("%d", b);
}

8874FA7A-3F01-EB9B-7122-B2D50020016E.png

通过反汇编可以看到,局部变量a的值10通过寄存器交给了调用的b,所以即使局部变量a空间的10已经被修改,也不会影响b的内容:

D7D796FF-5892-C8EB-7DE2-6A7079A6D03F.png

return只读数据段和static数据

如果返回只读字符串则不会被覆盖,因为数据不是在栈区,而是在静态区:

800B72F2-4821-C302-42A7-EDA3FEB811E6.png

同理如果将字符串用static修饰也是如此:

0B6443FF-1DA0-31AB-C6DF-6BF534547C86.png

另外,返回堆内的指针也是可以的。

收藏 (0)
评论列表
正在载入评论列表...
我是有底线的
为您推荐
    暂时没有数据