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

//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函数&&生命周期的一些见解的更多相关文章

  1. React 函数生命周期

      React 函数生命周期基础 1 ,概念 在组件创建.到加载到页面上运行.以及组件被销毁的过程中,总是伴随着各种各样的事件,这些在组件特定时期,触发的事件,统称为组件的生命周期:* 2,组件生命周 ...

  2. Vue钩子函数生命周期实例详解

    vue生命周期简介 Vue实例有一个完整的生命周期,也就是从开始创建.初始化数据.编译模板.挂载Dom.渲染→更新→渲染.卸载等一系列过程,我们称这是Vue的生命周期.通俗说就是Vue实例从创建到销毁 ...

  3. rust 函数-生命周期

    记录一下自己理解的生命周期. 每个变量都有自己的生命周期. 在c++里生命周期好比作用域, 小的作用域的可以使用大作用域的变量. 如果把这里的每个作用域取个名,那么就相当于rust里的生命周期注解. ...

  4. Vue系列之 => 钩子函数生命周期

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Unity 脚本函数生命周期

    Awake(),一般我们在这里做一些组件的获得,比如使用getcomponent方法. Start(),我们可以在这里给变量赋值. FixUpdate(),固定更新,因为这里得更新速度为固定(可以在T ...

  6. 【vue】钩子函数生命周期

    图1 图2: 图3 相关资料:http://www.zhimengzhe.com/Javascriptjiaocheng/236707.html    https://segmentfault.com ...

  7. Unity脚本中各函数成员的生命周期

    在学习Unity时,掌握如何编写脚本是必须掌握的一项基本技能.但是关于Unity的游戏脚本中各函数的生命周期是怎样开始和结束的,它们的执行顺序是如何安排的?这一点我们要清楚的了解. 我们知道Unity ...

  8. JavaScript巩固篇(一)——数据类型与全局变量、局部变量、垃圾回收机制、存储方式、生命周期

    知识要点 数据类型 存储方式 全局变量与局部变量 变量的生命周期 垃圾回收机制 知识概览 数据类型 JavaScript的数据类型分为:基本类型.引用类型 本质区别: 基本数据类型的变量实际值存储在栈 ...

  9. shell中的数据生命周期scope

    #!/bin/shexit 0#shell 中, 默认所有的变量都是 全局变量,除非主动变量前面加 local 修饰#shell 变量是字符变量,只能放字符和数字,shell数组也是如此;而数字也是图 ...

随机推荐

  1. iOS开发,用了ARC,引入非ARC的第三方,报错

    ARC forbids explicit message sendof'dealloc' 在xcode中,打开“Build Setting”,找到“Objective-C Automatic Refe ...

  2. sql表结构和注释

    SELECT 表名=case when a.colorder=1 then d.name else '' end, 表说明=case when a.colorder=1 then isnull(f.v ...

  3. iOS8: Ignore manifest download, already have bundleID

    在企业分发的app下载过程中,iOS8发现挂在官网上的企业版的app点击了提示是否安装应用程序,但始终安装不上程序,的device console发现安装的时候出现 LoadExternalDownl ...

  4. 29 个你必须知道的 Linux 命令

    虽然Linux发行版支持各种各样的饿GUI(graphical user interfaces),但在某些情况下,Linux的命令行接口(bash)仍然是简单快速的.Bash和 Linux Shell ...

  5. 数据结构与算法分析-AVL树

    1.AVL树是带有平衡条件的二叉查找树. 2.AVL树的每个节点高度最多相差1. 3.AVL树实现的难点在于插入或删除操作.由于插入和删除都有可能破坏AVL树高度最多相差1的特性,所以当特性被破坏时需 ...

  6. JS实现HashMap

    /** * ********* 操作实例 ************** * var map = new HashMap(); * map.put("key1","Valu ...

  7. HTML5新标签video在iOS上默认全屏播放

    今天做一个app时发现一个问题,应用html5中的video标签加载视频,在Android手机上默认播放大小,但是换成iPhone手机上出问题了,默认弹出全屏播放,查找了好多论坛,都没有谈论这个的.然 ...

  8. html的基础标签

    完整的网页由html嵌套 head.body构成 实例: <!DOCTYPE html><html lang="en"> <head> < ...

  9. 基于注解的SpringMVC简单介绍

    SpringMVC是一个基于DispatcherServlet的MVC框架,每一个请求最先访问的都是DispatcherServlet,DispatcherServlet负责转发每一个Request请 ...

  10. 配置JDK环境变量,与各步骤的意义

    配置JDK环境变量 1,新建变量名:JAVA_HOME,变量值:C:\Program Files\Java\jdk1.7.0                   (变量值为jdk安装路径) 2,打开P ...