转 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 ] ...
随机推荐
- Class-reference types 类引用类型--快要失传的技术
先摘一段原版的说明: A class-reference type, sometimes called a metaclass, is denoted by a construction of the ...
- mongodb的优缺点
在这里收集下我自己对Mongodb的一些优缺点方面的认识,或者是通过其它比较可靠的网文上引用或者摘录的作为依据,这个是一个渐进的过程,也是随着我对Mongodb认识的加深而不断扩展的. (1)Mong ...
- flume 详细介绍
http://blog.csdn.net/a2011480169/article/details/51544664 配有详细的例子. http://www.cnblogs.com/gongxijun/ ...
- 26.如何使用python操作我们自己创建的docker image呢?
因为逻辑复杂 我们建个文件来 python #是单行注释 '''是多行注释 或者””” 我们想使用python来操作docker 那么就要一个api https://github.com/docker ...
- Java解决小孩围圈问题
问题描述:一堆小孩围成一个圈,从第一个小孩开始数,每数到第三个便把第三个孩子删去,数到只剩一个小孩为止,并求该孩子的具体编号. 解决办法 1. package test; public class C ...
- ceph部署手册
CentOS7.2部署Luminous版Ceph-12.2.0 在CentOS7.2上安装部署Luminous版Ceph-12.2.0.由于ceph的Luminous版本默认使用bluestore作为 ...
- mysql 和 Oracle 数据类型对照
MySQL与Oracle两种数据库在工作中,都是用的比较多的数据库,由于MySQL与Oracle在数据类型上有部分差异,在我们迁移数据库时,会遇上一定的麻烦,下面介绍MySQL与Oracle数据库数据 ...
- rect用法
//如果创建一个Rect对象rect(100, 50, 50, 100),那么rect会有以下几个功能: rect.area(); //返回rect的面积 5000 rect.size(); //返回 ...
- DNS域名解析中A、AAAA、CNAME、MX、NS、TXT、SRV、SOA、PTR各项记录的作用
名注册完成后首先需要做域名解析,域名解析就是把域名指向网站所在服务器的IP,让人们通过注册的域名可以访问到网站.IP地址是网络上标识服务器的数字地址,为了方便记忆,使用域名来代替IP地址.域名解析就是 ...
- depth: working copy\infinity\immediates\files\empty
depth: working copy\infinity\immediates\files\empty 有时间,需要整理下,svn 合并深度这四项:具体的意思.