局部变量&&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函数&&生命周期的一些见解的更多相关文章
- React 函数生命周期
React 函数生命周期基础 1 ,概念 在组件创建.到加载到页面上运行.以及组件被销毁的过程中,总是伴随着各种各样的事件,这些在组件特定时期,触发的事件,统称为组件的生命周期:* 2,组件生命周 ...
- Vue钩子函数生命周期实例详解
vue生命周期简介 Vue实例有一个完整的生命周期,也就是从开始创建.初始化数据.编译模板.挂载Dom.渲染→更新→渲染.卸载等一系列过程,我们称这是Vue的生命周期.通俗说就是Vue实例从创建到销毁 ...
- rust 函数-生命周期
记录一下自己理解的生命周期. 每个变量都有自己的生命周期. 在c++里生命周期好比作用域, 小的作用域的可以使用大作用域的变量. 如果把这里的每个作用域取个名,那么就相当于rust里的生命周期注解. ...
- Vue系列之 => 钩子函数生命周期
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Unity 脚本函数生命周期
Awake(),一般我们在这里做一些组件的获得,比如使用getcomponent方法. Start(),我们可以在这里给变量赋值. FixUpdate(),固定更新,因为这里得更新速度为固定(可以在T ...
- 【vue】钩子函数生命周期
图1 图2: 图3 相关资料:http://www.zhimengzhe.com/Javascriptjiaocheng/236707.html https://segmentfault.com ...
- Unity脚本中各函数成员的生命周期
在学习Unity时,掌握如何编写脚本是必须掌握的一项基本技能.但是关于Unity的游戏脚本中各函数的生命周期是怎样开始和结束的,它们的执行顺序是如何安排的?这一点我们要清楚的了解. 我们知道Unity ...
- JavaScript巩固篇(一)——数据类型与全局变量、局部变量、垃圾回收机制、存储方式、生命周期
知识要点 数据类型 存储方式 全局变量与局部变量 变量的生命周期 垃圾回收机制 知识概览 数据类型 JavaScript的数据类型分为:基本类型.引用类型 本质区别: 基本数据类型的变量实际值存储在栈 ...
- shell中的数据生命周期scope
#!/bin/shexit 0#shell 中, 默认所有的变量都是 全局变量,除非主动变量前面加 local 修饰#shell 变量是字符变量,只能放字符和数字,shell数组也是如此;而数字也是图 ...
随机推荐
- Thinkphp_基础(2)URL模式
URL请求 ThinkPHP采用单一入口模式访问应用,对应用的所有请求都定向到应用的入口文件,系统会从URL参数中解析当前请求的模块.控制器和操作,下面是一个标准的URL访问格式: http://se ...
- MVC5 CodeFirst (一)
创建一个MVC5项目,VS生成了AccountController.HomeController,F5直接运行,实现了注册.登录.修改……但是注册的用户只是在内存里,下面我们来持久化. 三个命令:en ...
- [抓紧小长假的尾巴] 分析一个KeyFileMe
系统 : Windows xp 程序 : keyfileme 程序下载地址 :http://pan.baidu.com/s/1qYVfvu0 要求 : 编写KeyFile 使用工具 : OD 可在看雪 ...
- Life of a triangle - NVIDIA's logical pipeline
Home GameWorks Blog Life of a triangle - NVIDIA's logical pipeline Life of a triangle - NVIDIA's l ...
- android操作线程各种方法解析
(一)刚开始学习android的时候我是这么写的 new Thread( new Runnable() { public void run() { myView.invalidate(); } }). ...
- pb将datawindow数据导出EXCEL
PB有dw有自带功能可以将数据导出成excel,但是head却是字段名称,这样不直观. 所见即所得的方式是,将dw保存成html然后将后缀名改成xls,但这样的方式还有些问题就是会错列,解决的方法是将 ...
- 使用canvas元素-art方法绘制圆弧
最近在学习HTML5,发现canvas真的很棒,canvas元素是一种可供绘图的平面,我们用JavaScript对它进行配置和操作.我这里说一下arc方法绘制圆弧,顺便提一下涉及到的基础知识. 首先看 ...
- new date() 在Linux下引起的时间差问题
java工程部署到Linux时,使用new date()获取的时间出现时间差,通过查阅资料,发现有可能是服务器时间设置问题,JVM问题,jdk问题: 1.服务器时间设置问题: 正确的时间显示 有 CS ...
- C#的类及应用
1.int i=x.Length;//Length(获取字符串的长度,返回一个int类型的值) 2.x=x.Trim();(去掉字符串前后的空格) trimStart();(去掉前面的空格) Trim ...
- documentfragment
JS临时容器,父类是null,存储实际是把存储对象所有子类存储在里面,localStorage,浏览器支持情况下,保存本地变量