局部变量&&malloc函数&&生命周期的一些见解

Posted Excaliburer`s Zone

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了局部变量&&malloc函数&&生命周期的一些见解相关的知识,希望对你有一定的参考价值。

最近在温习指针的部分时发现了一个有趣的问题,先看以下程序:

//1.c
#include<stdio.h>
int* fun()
{
    int t = 567;
    return &t;
}
int main()
{
    int *p;
    p = fun();
    printf("%d",*p);
}

当我把1.c运行后,发现输出结果是:567。此时编译器给出警告信息:返回值是局部变量的地址。

    首先,我们知道操作系统给函数分配的内存空间都是在栈中,当函数调用结束后,操作系统就会回收其内存空间。当然,这个过程包括回收函数内部的局部变量(局部变量也是存储在栈空间中),而此处的"回收"是指销去变量名/函数名,并把其内存空间标记为可用。即操作系统可对该部分内存空间重新利用。但既然变量已经被回收了,为什么还能输出其值?

  对于此问题,我是这样认为的:

1.当变量的生命周期结束后,虽然变量该变量已不存在,但其之前分配的内存空间还在,也就是其地址还是之前变量的地址,而如果该部分内存空间在main函数结束之前都一直未被操作系统重新利用的话,它的数据没有被改写,那么此时输出*p的值还是之前变量的值。

2.虽然我输出的*p是567,但是不能保证每次输出的*p都是567。因为编译器和操作系统都无法保证这部分内存空间在main函数结束前一直都不会被利用,所以这种输出是不稳定的。实际上,此时的指针p相当于一个野指针,此时虽然也可以对这部分内存空间进行读写操作(可看作对野指针的操作),但是相当危险的。

3.由此可知,一定不要把局部变量的地址作为函数的返回值。

接下来再看另一个程序:

//2.c
#include<stdio.h>
#include<stdlib.h>
int* fun()
{
    int *t = (int*)malloc(sizeof(int)); ;
    *t = 567;
    return t;
}
int main()
{
    int *p;
    p = fun();
    printf("%d",*p);
}

  

当我运行2.c后,发现其输出结果:567。并且编译器没有给出任意的警告信息。

此时t作为一个局部变量,并且函数的返回值为t的地址,为什么编译器没给出警告?

在我看来,2.c中的t与1.c中t虽然同为局部变量,两者在内存中的分布是完全不同的。1.c中的t是存储在栈空间中的,而2.c中t是存储在堆空间中的(堆与栈的区别:http://www.cnblogs.com/wangkundentisy/articles/6003482.html)。而当局部变量生命周期结束后,编译器会先消除该局部变量名,然后对于栈中的空间,编译器会释放它;而对于堆中的空间,编译器并不理会。所以,在2.c中,虽然fun函数以及指针t被销毁,但t指向的内存空间依然完好无损,并且由于编译器并未释放它,操作系统自然不会去利用这部分内存空间。所以,最后输出*p时,始终会输出567。但是,这样会造成内存泄漏,并产生垃圾!所以说malloc之后,一定要有一个free与之相对应!故在2.c中,printf函数后应加上free(p)。

  综上,在写程序时,一定不要把局部变量的地址作为函数的返回值!一定尽量避免返回在函数内部使用的分配函数(malloc或new)分配的内存空间,以及malloc和free一定要成对的出现!

以上是关于局部变量&&malloc函数&&生命周期的一些见解的主要内容,如果未能解决你的问题,请参考以下文章

C_局部变量&全局变量

局部变量&全局变量

局部变量的引用折叠

自动对象&局部静态对象

R中的全局变量和局部变量

成员变量(实例变量)&局部变量&静态变量(类变量)的区别