讨论:C++中函数不能返回局部变量的引用,你是怎么理解的?

一蜉蝣 2012-07-29 11:57:46
C++ Primer中 214页中说千万不要返回局部变量的引用,然后有下面的示例:
const string &mainp(const string &s)
{
string ret=s;
return ret
}
//这个函数在运行的时候会出错,因为它返回了局部变量的引用。
我有点不明白 return ret 分明是返回的局部变量嘛 怎么会是返回局部变量的引用呢?

书上说不能返回局部变量的应用是因为函数在执行完后,系统就释放了局部变量的存储空间。有一点不太明白:函数执行完不就是执行完return语句才叫做执行完吗?那么执行return语句的时候函数还没有执行完。那么局部变量也就没有被释放 那应该还是可以引用的吧。

要是我哪里理解错了的话,请看下面的例子:

#include<iostream>
using namespace std;
int &ref()
{
int ivar=1100; //局部变量
int &irvar=ivar;
return irvar; // 返回了局部变量的引用
}
int main(int argc,char *argv[])
{
cout<<ref();
}

我编译后是可以正确运行的。
请大家解惑:
...全文
1662 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
int8 2014-05-15
  • 打赏
  • 举报
回复
函数返回时的过程是这样的: 函数计算完毕,执行到return语句时,会在内存中自动定义一个临时变量以return的值初始化,之后函数执行完毕,局部变量释放,注意此时临时变量还存在!然后用户根据需要访问或者不访问临时变量,等到执行下一条语句,临时变量自动销毁。 所以不能返回局部变量的引用,因为临时变量指向的内存已经释放! 以下是例证: vs2010

#include <iostream>
using namespace std;

class A
{
public:
	A()
	{
		cout<<"无参构造函数"<<endl;
	}
	A(const A& a)
	{
		cout<<"拷贝构造函数"<<endl;
	}
	A& operator = (const A& a)
	{
		cout<<"赋值运算符重载"<<endl;
		return *this;
	}
	~A()
	{
		cout<<"析构函数"<<endl;
	}
private:

};
const A boo(const A a)
{
	cout<<"A tmp = a"<<endl;
	A tmp = a;//调用拷贝构造函数
	cout<<"return tmp"<<endl;
	return tmp;//外部会自动定义一个临时变量,以tmp初始化,所以调用拷贝构造函数
}
int main(int argc,char* argv[])
{
	cout<<"A a"<<endl;
	A a;//调用无参构造函数
	cout<<"A b(a)"<<endl;
	A b(a);//调用拷贝构造函数
	cout<<"-----------------------------------------------"<<endl;
	b=boo(a);//当把a值传递给形参时,会调用拷贝构造函数;返回时,会定义临时变量以函数内返回值初始化
	system("pause");
	return 0;
}
执行结果: A a 无参构造函数 A b(a) 拷贝构造函数 ----------------------------------------------- 拷贝构造函数 A tmp = a 拷贝构造函数 return tmp 拷贝构造函数 析构函数 析构函数 赋值运算符重载 析构函数 请按任意键继续. . .
过野秋风 2013-12-14
  • 打赏
  • 举报
回复
引用 15 楼 yifuyou 的回复:
[Quote=引用 12 楼 的回复:] 刚才我说的话是从书上搬来的。我用vs2010的编译器试了一下,发现确实能通过。 好吧,我现在也不会了。 [/Quote] 我现在好想明白点了:C++ Primer上说的是不能返回局部变量的引用。我觉得这一点说的不太确切。应该改为:不能返回局部变量的引用给一个引用变量。但是如果返回引用给一个相关的非引用变量的话是可以的。下面的例子可以说明这一点。 #include<iostream> using namespace std; int &ref() { int ivar=1100; //局部变量 int &irvar=ivar; return irvar; // 返回了局部变量的引用 } void test() { int a=b=c=d=e=f=g=h=0; } int main() { /*这里函数ref返回局部变量的引用,返回后立即把ivar的值重新赋给了变量i,在主函数中i//是有效的,所以能够得到正确的结果*/ int i=ref(); int &j=ref(); //而下面这句,函数ref返回一个引用给引用变量。所以j就是ivar的别名 test(); //此处调用函数test,则会覆盖已经被系统释放的ivar所占的内存空间。 // 如果注释掉这句会发现下面输出j仍然会得到正确的结果 cout<<i<<endl<<j<<endl; } 我是在vc6.0中运行的 没注释掉test的时候 输出j得到的是一个很大的随机值 注释掉test后仍然能够得到1100;所以我觉得书上的那句话有必要改一改:可以返回局部变量的引用,但是千万不要反回局部变量的引用给一个引用变量。
解释的很正确,但是C++primer上的那句话是没错的,确实不能返回局部变量的引用,你的这种不报错的情况只适用于int,double等值类型的,如果你让他返回一个list,vector等这种类型的运行时候是直接会报错的,例如: #include<iostream> #include<list> using namespace std; list<int>& reset() { int j=10; list<int> newlist; while(j>0) { newlist.push_back(j); j--; } return newlist; } int main() { list<int> mylist; //list<int>& mylist; //这个也是一样报错 mylist=reset(); for(list<int>::iterator iter=mylist.begin();iter!=mylist.end();iter++) { cout<<*iter<<endl; } system("pause"); return 0; } 这个程序跑起来就会报内存错误,因为无论是赋给引用的或者非引用的变量,在调用函数返回后,newlist的值都被list的析构函数中的clear()清除了,即newlist的指向都是未知的,所以报内存错误!在VS2010中亲测。 大部分类都会定义这样的析构函数来释放类中的定义的指针,所以C++Primer中说的也没错,因为它主要考虑的是面向对象程序设计的情况,不过那些值类型的确实有些例外。
neulin 2013-01-28
  • 打赏
  • 举报
回复
引用 15 楼 yifuyou 的回复:
引用 12 楼 的回复:刚才我说的话是从书上搬来的。我用vs2010的编译器试了一下,发现确实能通过。 好吧,我现在也不会了。 我现在好想明白点了:C++ Primer上说的是不能返回局部变量的引用。我觉得这一点说的不太确切。应该改为:不能返回局部变量的引用给一个引用变量。但是如果返回引用给一个相关的非引用变量的话是可以的。下面的例子可以说明这一点。 #i……
我是一个新手,你说的这个我没看明白,只是觉得局部变量没有被立刻销毁,用int i=ref()的时候,值已经被存起来了,而test()的时候,我想是将没用的局部变量的内存覆写了,所以也能理解int& j的结果吧,希望高手指点那。
jianghua327710 2012-10-25
  • 打赏
  • 举报
回复
如果是引用类型的话,返回的是地址,主函数根据地址获取值,如果是局部变量的话,虽然获取到地址,但此时该地址的内容被释放了,如果是局部变量就可以了
一蜉蝣 2012-07-30
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 的回复:]
因为局部变量或者局部指针所指的内容(在函数内部,用静态方式分配的内存)在函数返回后,就失效了。

以局部指针为例,局部指针本身是能够正常的返回给调用者的,但是局部指针所指向的内容在函数返回后就没有意义了,也就是说调用所得到的指针,它所指向的内容是没有意义的东西。

可以参考:
关于函数返回值的几种情况
[/Quote]

太谢谢你了 豁然开朗
一蜉蝣 2012-07-30
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 的回复:]
刚才我说的话是从书上搬来的。我用vs2010的编译器试了一下,发现确实能通过。
好吧,我现在也不会了。
[/Quote]

我现在好想明白点了:C++ Primer上说的是不能返回局部变量的引用。我觉得这一点说的不太确切。应该改为:不能返回局部变量的引用给一个引用变量。但是如果返回引用给一个相关的非引用变量的话是可以的。下面的例子可以说明这一点。

#include<iostream>
using namespace std;
int &ref()
{
int ivar=1100; //局部变量
int &irvar=ivar;
return irvar; // 返回了局部变量的引用
}

void test()
{
int a=b=c=d=e=f=g=h=0;
}

int main()
{
/*这里函数ref返回局部变量的引用,返回后立即把ivar的值重新赋给了变量i,在主函数中i//是有效的,所以能够得到正确的结果*/
int i=ref();
int &j=ref(); //而下面这句,函数ref返回一个引用给引用变量。所以j就是ivar的别名
test(); //此处调用函数test,则会覆盖已经被系统释放的ivar所占的内存空间。
// 如果注释掉这句会发现下面输出j仍然会得到正确的结果
cout<<i<<endl<<j<<endl;
}

我是在vc6.0中运行的 没注释掉test的时候 输出j得到的是一个很大的随机值 注释掉test后仍然能够得到1100;所以我觉得书上的那句话有必要改一改:可以返回局部变量的引用,但是千万不要反回局部变量的引用给一个引用变量。

一蜉蝣 2012-07-30
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 的回复:]
因为局部变量或者局部指针所指的内容(在函数内部,用静态方式分配的内存)在函数返回后,就失效了。

以局部指针为例,局部指针本身是能够正常的返回给调用者的,但是局部指针所指向的内容在函数返回后就没有意义了,也就是说调用所得到的指针,它所指向的内容是没有意义的东西。

可以参考:
关于函数返回值的几种情况
[/Quote]

但是为什么我的例子运行出来是正确的呢?
iamnobody 2012-07-29
  • 打赏
  • 举报
回复
换个说法就是,
当返回值是引用类型时,该引用返回值不能绑定到局部对象.
一蜉蝣 2012-07-29
  • 打赏
  • 举报
回复
谁来解答一下啊
一蜉蝣 2012-07-29
  • 打赏
  • 举报
回复
例子敲错了:返回语句少敲了一个分号:return ret;
nimingzhe2008 2012-07-29
  • 打赏
  • 举报
回复 1
Google了一下英文网站,有人对此的解释是“操作系统还没有立刻将本地变量的内存分配给其他变量,但这是暂时的、不稳定的。”
nimingzhe2008 2012-07-29
  • 打赏
  • 举报
回复
刚才我说的话是从书上搬来的。我用vs2010的编译器试了一下,发现确实能通过。
好吧,我现在也不会了。
nimingzhe2008 2012-07-29
  • 打赏
  • 举报
回复
位于一对{}中的语句叫做block,而在block中声明的变量叫做本地变量,它只在{}中有效。
引用的背后是通过指针实现的。若函数返回本地变量的引用,即代码背后返回一个指针,该指针指向一个已经失效的变量。显然这是错误的
幸运的是,编译器会对这种代码报错。
sliven_ji 2012-07-29
  • 打赏
  • 举报
回复
引用是指一个变量的别名,其本质上其实也是指针,只是不能改变其指向的地址而已。局部变量在退出函数时会释放内存,生命周期结束。而返回的引用实质是返回局部变量的地址。此时该地址下的内存空间已不再属于原来的局部变量了。有可能已分配给别的变量,因此再通过引用来操作该内存空间是不对的。
zhs077 2012-07-29
  • 打赏
  • 举报
回复
局部变量生命周期,当返回后会自动销毁,而返回引用的话,会使用一个已经销毁的对象,这样是不行的
baixiaojin89 2012-07-29
  • 打赏
  • 举报
回复
楼主C++基础不行啊,const string & mainp()返回引用不是看return返回什么而是看函数的返回类型,这明显是引用嘛!
函数在return执行时是没有完,但你要知道引用传递是传递地址,返回也是返回地址,你是把地址给了人家,函数调用结束后地址上的内容就销毁了,所以你指向的内容有可能不存在,或者被别人用了,这样是不可以的。楼主没有明白引用的本质。
pathuang68 2012-07-29
  • 打赏
  • 举报
回复
因为局部变量或者局部指针所指的内容(在函数内部,用静态方式分配的内存)在函数返回后,就失效了。

以局部指针为例,局部指针本身是能够正常的返回给调用者的,但是局部指针所指向的内容在函数返回后就没有意义了,也就是说调用所得到的指针,它所指向的内容是没有意义的东西。

可以参考:
关于函数返回值的几种情况
sadgod 2012-07-29
  • 打赏
  • 举报
回复
局部变量存放在栈上,函数返回后,这段空间被复用了,存在这里是不靠谱的。
hen_hao_ji 2012-07-29
  • 打赏
  • 举报
回复
chanhit 2012-07-29
  • 打赏
  • 举报
回复
所谓引用可以认为是变量的别名,局部变量在函数退出后就销毁了,即便return的时候有效,但是一出来,调用者得到的就是一个无效的东东了,另外楼主的例子用自定义类型试一下就知道了
学习计划:1、每天学习1个小时,大概1个月完成。 2、认真听讲,要自己揣摩,明白原理。 3、动手实践、必须要多做课后练习题,遇到不会的,及时提问。课程目标:从一个纯小白,到真正理解变量的实质,指针的实质,学会C++类及面向对象编程课程简介:敬告:该系列的课程在抓紧录制更新,敬请大家关注。该系列的课程涉及:FFmpeg,WebRTC,SRS,Nginx,Darwin,Live555,等。包括:音视频、流媒体、直播、Android、视频监控28181、等。---------------------------------------------------  ---------------------------------------------------给学员(从小白到大牛)的3年学习路径及计划技术方面分三块:1.纯开发技术方向2.音视频流媒体专业方向3.项目实战---------------------------------------------------1.纯开发技术方向(1) C++必须要过硬(至少学会10本经典好书)(2) 系统级编程(Windows、Linux),必须特别熟练系统API,灵活运用(3) 框架与工具(Qt、MFC):必须精通其一种。(4) 架构与设计模式:需要提升一个高度,不再是简单的编码,而是思维模式。(5) 驱动级别(如果有兴趣,可以深入到驱动级:包括Windows、Linux)(6) 最好学习点Java+Html+javascript等WEB技术。2.音视频流媒体专业方向(1) 音视频流媒体基础理论:    必须认真学会,否则看代码就是看天书(2) 编解码方向:精通h.264,h.265(hevc), 包括理论和各个开源库(ffmpeg,libx264,libx265,...)。(3) 直播方向:  精通各种直播协议(rtsp,rtmp,hls,http-flv,...), 钻研各个开源库(live555,darwin,srs,zlmediakit,crtmpserver,...)(4) 视频监控:  理论+开源库(onvif+281818)(EasyMonitor、iSpy、ZoneMinder(web)、...)  3.项目实战(1) Qt项目:  至少要亲手练习10个实战项目(网络服务器、多线程、数据库、图像处理、多人聊天、等等)(2)音视频项目:包括编解码、视频监控、直播等各个方向,都需要亲手实战项目,包括视频服务器、后台管理系统、前端播放器(多端)---------------------------------------------------  具体的内容包括: n 1.全书概览与分析 51.1 初步认识:做到心有数 51.2 图解:章节关系 61.3 探讨:教学方法论 71.4 探讨:学习方法论 71.5 C++的1.0与2.0 81.6 开发工具Qt5.9和VS2015/7/9 8控制台: 8UI界面: 81.7 安装并搭建Qt5.9.8开发环境 91.下载软件Qt Creator 5.9.8 92.双击开始安装 93.注册Qt账号 114.激活Qt账号 125.选择安装路径 176.选择“安装组件” 197.同意“许可协议” 218.点击“安装”后,请喝杯茶 241.8 捋起袖子开干:几个小案例 35第一:写代码打印“CS”图形 36第二:写代码打印各种图形(作业) 361.9 原书第一章剖析 371.计算机是什么? 372.程序是什么? 383.程序设计 384.C++入门程序示例 40n 第2章 C++基础知识 412.1 变量和赋值 412.2 输入和输出 422.3 数据类型和表达式 432.4 简单控制流程 442.5 程序风格 452.6 x=x-3到底是个什么鬼? 46第3章 更多的控制流程 473.1 使用布尔表达式 473.2 多路分支 483.3 C++循环语句详解 483.4 设计循环 513.5 课堂练习 51第4章 过程抽象和返回值的函数 524.1 自顶向下设计 524.2 预定义函数 534.3 程序员自定义函数 534.4 过程抽象 544.5 作用域和局部变量 554.6 重载函数名称 55第5章 用函数完成所有子任务 565.1 void函数 575.2 传引用参数 575.3 使用过程抽象 595.4 测试和调试函数 595.5 常规调试技术 59第6章 I/O流——对象和类入门 606.1 流和基本文件I/O 606.2 流I/O工具 616.3 字符I/O 62第7章 数组 637.1 数组入门 637.2 函数的数组 657.3 数组编程 667.4 多维数组 67第8章 字符串和向量 688.1 字符串的数组类型 688.2 标准string类 698.3 向量 71第9章 指针和动态数组 719.1 指针 719.2 手工动态数组 74第10章 定义类 7610.1 结构体 7710.2 类 7810.3 抽象数据类型 8110.4 继承 8210.5 拷贝构造函数 8310.6 析构函数 8410.7 重载赋值(=)操作符 84第11章 友元函数、重载操作符和数组 8611.1 友元函数 8711.2 重载操作符 8811.3 数组和类 9111.4 类和动态数组 91第12章 独立编译和命名空间 9212.1 独立编译 9212.2 命名空间 93第13章 指针和链表 94第14章 递归 95第15章 继承 9615.1 继承基础 9615.2 继承细节 9715.3 多态性(虚函数) 98第16章 异常处理 10016.1 异常处理基础 10016.2 用于异常处理的编程技术 101第17章 模板 10217.1 用于算法抽象的模板 10217.2 用于数据抽象的模板 103第18章 标准模板库(STL) 10418.1 迭代器 10418.2 容器 10518.3 泛型算法 10618.4 不断进化的C++ 106第19章 Qt界面开发入门 106附录 107 

64,639

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧