转 C++函数返回值,你必须注意的问题
归根结底,C++所面临的问题要求它提供各种各样的机制以保证性能,也许,这辈子也见不到C++能安全有效的自己进行内存垃圾回收。。。。。
老程序猿都会提醒菜鸟,注意函数的返回值,因为,很可能,你的函数返回的数据在后续的使用中会出错。那么函数在返回值时要注意什么呢?
本篇博客尝试用最简练的普通大白话,讲解函数返回值的问题。
C++把内存交给了程序猿,但是,请你注意,它可没把所有的内存都交给你,交给你的只是堆上的内存,也就是你通过malloc函数 和new 关键字申请来的内存,除了这些内存以外,其他的内存,你最好别碰,最好别碰,最好别碰,重要的事情说三遍。
如果你的函数返回值在后续使用中出错了,尤其是返回函数内的局部变量这种事情,那么,基本可以肯定,你碰了不该碰的内存。这时候,你会觉得自己很冤枉啊,我没有啊。但事实是,没有冤枉你,所以,为了不被bug检察院起诉你,作为一个C++程序猿,你必须学会甄别那些内存是能碰的,那些内存是不能碰的。
- char *pstr = "This is the buffer text";
- return pstr;
如果你的函数是这么写的,那么恭喜你,返回正确,因为这个pstr指向的是常量存储区,这里的内存,你是可以碰的,但是注意,这个碰,仅仅是读,你想修改,那是万万不可以的。
- char buffer[] = "This is the buffer text";
- return buffer;
如果你的函数是这么写的,那么恭喜你,等着bug联邦检察院起诉你吧。这里的buffer指向的是栈上内存,这个,就是你碰不得的,前面的pstr就好比公园,公园嘛,大家都可以来玩,但是你不能把公园里的假山拆了,你也不能把公园里的树砍了,你只能是来玩,不能修改它,栈上的内存,就好比是私家花园,你一个外人,是不能进去的。那么怎么甄别的,方法倒也简单,你见到带中括号的,就应该明白,这东西是栈上的,出了这个函数,你就别想再碰的,你只要敢碰,bug联邦检察院就会起诉你。
- static char buffer[] = "This is the buffer text";
- return buffer;
如果你的函数是这么写的,那么恭喜你,返回正确,可是刚才不是明明说,这里是私家花园嘛,没错,但是你注意看,前面还加了一个static,只要加了这个关键字,就相当于说国家把这个私家花园征用了,那么,它就从私家花园变成了静态存储区里的一个小花园,静态存储区里的内存,国家说,静态存储区对外开放,你们都可以来。
函数返回的都是值拷贝,栈上的内存,在函数结束的时候,都会被收回。在函数内部,你可以碰栈上的内存,那是因为这个时候你是在栈的家里做客,那他们家的内存小花园当然允许你访问,可是函数结束了,就相当于你离开了栈的家,栈把内存小花园的门关上了,你怎么可以进去,你进去了,就会被bug联邦法院起诉!
但是呢,总有一些奇怪的现象让你以为你可以在函数结束后仍然可以访问栈上的内存。
我们定义一个结构体
- struct person
- {
- int age;
- }
写一个函数
- person* getperson2()
- {
- person p;
- p.age = 99;
- return &p;
- }
在得到函数的返回值以后,你可以输出对象的年龄
- person *p2 = getperson2();
- cout<<p2->age<<endl;
你会发现,这段代码居然可以正确执行!在函数getperson2内部,p这个变量是局部变量,必然是在栈上申请的,返回的是&p,这不就是栈上的内存地址么,那为啥在函数外部,却仍然可以输出age呢?
虽然,函数结束后,对象被销毁,但是销毁的不够彻底,似乎计算机在管理内存时也不需要那么彻底的销毁一个对象,你之所以能输出age,那是因为那个区域,没有被彻底销毁,这一小块的内存(存储age的4个byte)没有发生变化。你可以暂时的碰这块内存,但迟早是要出问题的,如果某一刻,计算机打算用这块内存,发现你在非法使用,那么必然会报警,然后bug联邦检察院会起诉你。
为了让问题更透明一些,我们修改一下结构体
- struct person
- {
- int age;
- char* name;
- person()
- {
- name = new char(10);
- strcpy(name,"sheng");
- }
- ~person()
- {
- name = NULL;
- }
- };
- person* getperson2()
- {
- person p;
- p.age = 99;
- return &p;
- }
- person *p2 = getperson2();
- cout<<p2->age<<endl;
- cout<<p2->name<<endl;
这一次,函数结束后,对象的销毁要比上一次彻底的多,虽然,age的区域还是没有被彻底销毁,但是name区域被彻底销毁了,如果你访问name的区域,就必然出错,这就好比啊,私家花园关门了,可是花园好大的,所以不是每一处都安装了摄像头和报警器,比如age这片区域,所以,你偷偷的从age这个区域溜进去时,花园的主人没发现,直到花园的巡防大队到age区域巡防时,发现你竟然在这里偷偷菜花,结果就是把你打的崩溃了。而name这边区域,在~person这个析构函数中安装了摄像头和报警器,你只要来,就立刻报警,然后把你打的崩溃。
千言万语,汇成一句话,函数不要返回指向栈的内存地址,切记,是地址,别被吓的所有的函数内的变量都不敢返回,只要不是栈的内存地址,你尽管放心的返回。
转 C++函数返回值,你必须注意的问题的更多相关文章
- shell调用函数返回值深入分析
编写shell脚本过程中,我们经常会自定义一些函数,并根据函数的返回值不同来执行相应的流程,那么我们如何来获取函数的返回值呢? 首先shell中调用函数有两种方式: 第一种:value=`functi ...
- Python从线程获取函数返回值
Python中利用强大的threading模块可以很容易的实现多线程开发,提高运行速度.这一般是对某个进行大量计算操作的的函数进行多线程处理,然后合并各线程的结果.获取函数返回值的方法可以如下: 1) ...
- 速战速决 (3) - PHP: 函数基础, 函数参数, 函数返回值, 可变函数, 匿名函数, 闭包函数, 回调函数
[源码下载] 速战速决 (3) - PHP: 函数基础, 函数参数, 函数返回值, 可变函数, 匿名函数, 闭包函数, 回调函数 作者:webabcd 介绍速战速决 之 PHP 函数基础 函数参数 函 ...
- string类find函数返回值判定
string类find函数返回值判定 代码示例 #include<iostream> #include<cstring> using namespace std; int m ...
- c语言main函数返回值、参数详解(返回值是必须的,0表示正常退出)
C语言Main函数返回值 main函数的返回值,用于说明程序的退出状态.如果返回0,则代表程序正常退出:返回其它数字的含义则由系统决定.通常,返回非零代表程序异常退出. 很多人甚至市面上的一些书籍,都 ...
- Python学习教程(learning Python)--2.3.4Python函数返回值
本节讨论Python函数返回值问题. Python和C语言一样,也可以在函数结束时返回一个值.但在定义自己的Python函数时,是不需要指定返回值数据类型的,这和Python不关心变量的数据类型是一致 ...
- C++ const修饰函数、函数参数、函数返回值
const修饰函数 在类中将成员函数修饰为const表明在该函数体内,不能修改对象的数据成员而且不能调用非const函数.为什么不能调用非const函数?因为非const函数可能修改数据成员,cons ...
- Shell函数:Shell函数返回值、删除函数、在终端调用函数
函数可以让我们将一个复杂功能划分成若干模块,让程序结构更加清晰,代码重复利用率更高.像其他编程语言一样,Shell 也支持函数.Shell 函数必须先定义后使用. Shell 函数的定义格式如下: f ...
- PHP获取函数返回值的引用
通过在函数前添加&可以获取函数返回值的引用,如:function &test(){return 10;}
- Shell函数返回值、删除函数、在终端调用函数
Shell 也支持函数.Shell 函数必须先定义后使用. Shell 函数的定义格式如下: function_name () { list of commands [ return value ] ...
随机推荐
- delphi 原创应用工具箱
用到的主要知识点: (1) listview背景透明 (2) 读取应用图标 (3)图标透明 (4)实时显示微软必应首页图,裁剪图片等
- 阿里支付宝java接口
网上关于Java支付宝接口的文章很多,都大同小异,但是具体到代码中,还是不太一样,对于以前没有调试的新手来说还是很费解的,这是通过调试认为比较有用的版本,贴在这里供大家参考. 1.从本站提交到支付宝: ...
- 手机移动端web前端常见问题整理
移动端常见问题及解决方案 一.meta基础知识 H5页面窗口自动调整到设备宽度,并禁止用户缩放页面 <meta name="viewport" content="w ...
- ztree根据treeId展开指定节点并触发单击事件
ztree.expandNode(ztree.getNodeByParam("id",treeId,null));//展开指定节点 ztree.selectNode(ztree.g ...
- 学习JS的心路历程-范围Scope和提升(Hoisting)
在上一篇提到了JS有三种声明变量的方式,分别是var.const及let,var和const let最大区别就是范围(scope)的限制.所以在这一篇我们会详谈何谓范围链及他们的复写优先级. 范围Sc ...
- SAP 使用
SAP 提供多种方法查找系统内的事务代码 1. 使用SE11查看存储事物代码的表:TSTC 或者TSTCT TSTC: 存有事务代码,程序名称,屏幕号码等字段 TSTCT: 存有语言代码,事务代码,事 ...
- Redis安装完后redis-cli无法使用(redis-cli: command not found)已使用
wget http://download.redis.io/redis-stable.tar.gz(下载redis-cli的压缩包) tar xvzf redis-stable.tar.gz(解压) ...
- crontab -e文件存放路径
crontab -e结果存放在/var/spool/cron/crontabs中
- Java计算计算活了多少天
Java计算计算活了多少天 思路: 1.输入你的出现日期: 2.利用日期转换,将字符串转换成date类型 3.然后将date时间换成毫秒时间 4.然后获取当前毫秒时间: 5.最后计算出来到这个时间多少 ...
- ORA-01578 ORACLE data block corrupted (file # 29, block # 2889087)
BW数据库后台报错如下:F:\oracle\SBP\saptrace\diag\rdbms\sbp\sbp\trace ORA-01578: ORACLE data block corrupted ( ...