转 C++函数返回值,你必须注意的问题

归根结底,C++所面临的问题要求它提供各种各样的机制以保证性能,也许,这辈子也见不到C++能安全有效的自己进行内存垃圾回收。。。。。

老程序猿都会提醒菜鸟,注意函数的返回值,因为,很可能,你的函数返回的数据在后续的使用中会出错。那么函数在返回值时要注意什么呢?

本篇博客尝试用最简练的普通大白话,讲解函数返回值的问题。

C++把内存交给了程序猿,但是,请你注意,它可没把所有的内存都交给你,交给你的只是堆上的内存,也就是你通过malloc函数  和new 关键字申请来的内存,除了这些内存以外,其他的内存,你最好别碰,最好别碰,最好别碰,重要的事情说三遍。

如果你的函数返回值在后续使用中出错了,尤其是返回函数内的局部变量这种事情,那么,基本可以肯定,你碰了不该碰的内存。这时候,你会觉得自己很冤枉啊,我没有啊。但事实是,没有冤枉你,所以,为了不被bug检察院起诉你,作为一个C++程序猿,你必须学会甄别那些内存是能碰的,那些内存是不能碰的。

[cpp] view plain copy

  1. char *pstr = "This is the buffer text";
  2. return pstr;

如果你的函数是这么写的,那么恭喜你,返回正确,因为这个pstr指向的是常量存储区,这里的内存,你是可以碰的,但是注意,这个碰,仅仅是读,你想修改,那是万万不可以的。

[cpp] view plain copy

  1. char buffer[] = "This is the buffer text";
  2. return buffer;

如果你的函数是这么写的,那么恭喜你,等着bug联邦检察院起诉你吧。这里的buffer指向的是栈上内存,这个,就是你碰不得的,前面的pstr就好比公园,公园嘛,大家都可以来玩,但是你不能把公园里的假山拆了,你也不能把公园里的树砍了,你只能是来玩,不能修改它,栈上的内存,就好比是私家花园,你一个外人,是不能进去的。那么怎么甄别的,方法倒也简单,你见到带中括号的,就应该明白,这东西是栈上的,出了这个函数,你就别想再碰的,你只要敢碰,bug联邦检察院就会起诉你。

[cpp] view plain copy

  1. static char buffer[] = "This is the buffer text";
  2. return buffer;

如果你的函数是这么写的,那么恭喜你,返回正确,可是刚才不是明明说,这里是私家花园嘛,没错,但是你注意看,前面还加了一个static,只要加了这个关键字,就相当于说国家把这个私家花园征用了,那么,它就从私家花园变成了静态存储区里的一个小花园,静态存储区里的内存,国家说,静态存储区对外开放,你们都可以来。

函数返回的都是值拷贝,栈上的内存,在函数结束的时候,都会被收回。在函数内部,你可以碰栈上的内存,那是因为这个时候你是在栈的家里做客,那他们家的内存小花园当然允许你访问,可是函数结束了,就相当于你离开了栈的家,栈把内存小花园的门关上了,你怎么可以进去,你进去了,就会被bug联邦法院起诉!

但是呢,总有一些奇怪的现象让你以为你可以在函数结束后仍然可以访问栈上的内存。

我们定义一个结构体

[cpp] view plain copy

  1. struct person
  2. {
  3. int age;
  4. }

写一个函数

[cpp] view plain copy

  1. person*  getperson2()
  2. {
  3. person p;
  4. p.age = 99;
  5. return &p;
  6. }

在得到函数的返回值以后,你可以输出对象的年龄

[cpp] view plain copy

  1. person *p2 = getperson2();
  2. cout<<p2->age<<endl;

你会发现,这段代码居然可以正确执行!在函数getperson2内部,p这个变量是局部变量,必然是在栈上申请的,返回的是&p,这不就是栈上的内存地址么,那为啥在函数外部,却仍然可以输出age呢?

虽然,函数结束后,对象被销毁,但是销毁的不够彻底,似乎计算机在管理内存时也不需要那么彻底的销毁一个对象,你之所以能输出age,那是因为那个区域,没有被彻底销毁,这一小块的内存(存储age的4个byte)没有发生变化。你可以暂时的碰这块内存,但迟早是要出问题的,如果某一刻,计算机打算用这块内存,发现你在非法使用,那么必然会报警,然后bug联邦检察院会起诉你。

为了让问题更透明一些,我们修改一下结构体

[cpp] view plain copy

  1. struct person
  2. {
  3. int age;
  4. char* name;
  5. person()
  6. {
  7. name = new char(10);
  8. strcpy(name,"sheng");
  9. }
  10. ~person()
  11. {
  12. name = NULL;
  13. }
  14. };

[cpp] view plain copy

  1. person*  getperson2()
  2. {
  3. person p;
  4. p.age = 99;
  5. return &p;
  6. }

[cpp] view plain copy

  1. person *p2 = getperson2();
  2. cout<<p2->age<<endl;
  3. cout<<p2->name<<endl;

这一次,函数结束后,对象的销毁要比上一次彻底的多,虽然,age的区域还是没有被彻底销毁,但是name区域被彻底销毁了,如果你访问name的区域,就必然出错,这就好比啊,私家花园关门了,可是花园好大的,所以不是每一处都安装了摄像头和报警器,比如age这片区域,所以,你偷偷的从age这个区域溜进去时,花园的主人没发现,直到花园的巡防大队到age区域巡防时,发现你竟然在这里偷偷菜花,结果就是把你打的崩溃了。而name这边区域,在~person这个析构函数中安装了摄像头和报警器,你只要来,就立刻报警,然后把你打的崩溃。

千言万语,汇成一句话,函数不要返回指向栈的内存地址,切记,是地址,别被吓的所有的函数内的变量都不敢返回,只要不是栈的内存地址,你尽管放心的返回。

时间: 2024-10-05 20:48:46

转 C++函数返回值,你必须注意的问题的相关文章

以函数返回值做参数时,函数调用的顺序

环境:vs2013 在下面的代码中 1 //类似于下面的代码 2 3 foo(char*,char*,char*); 4 5 char* str ="A#B#C"; 6 7 foo(strtok(str,"#"),strtok(NULL,"#"),strtok(NULL,"#")); 预计让函数foo得到("A","B","C")的参数,程序编译的时候没问题,但是运行

函数指针与指针函数返回值的区别

指针函数是指带指针的函数,即本质是一个函数.函数返回类型是某一类型的指针定义: 类型标识符 *函数名(参数表)eg: int *f(x,y);函数指针是指向函数的指针变量,即本质是一个指针变量.int (*f) (int x); /* 声明一个函数指针 */ f=func; /* 将func函数的首地址赋给指针f */ 函数指针与指针函数返回值的区别,码迷,mamicode.com

对象做函数参数和函数返回值时,调用复制构造函数,构造函数,析构函数的情况

// 对象做函数参数和返回值.cpp : 定义控制台应用程序的入口点.//exit(0)表示正常退出程序,exit(0)表示异常退出 //在调用input时,编译器用对象A去创建了形参对象temp,调用了复制构造函数,对象A中的数据复制给了对象temp// 在input函数中,执行temp.set(s),为对象temp中数据成员str申请了动态储存空间,并设置了输入的字符串//并没有改变实参A中的数据成员str的储存空间,故在执行语句A.show()后输出的字符串并没有改变.在函数调用结束后 /

shell调用函数返回值深入分析

编写shell脚本过程中,我们经常会自定义一些函数,并根据函数的返回值不同来执行相应的流程,那么我们如何来获取函数的返回值呢? 首先shell中调用函数有两种方式: 第一种:value=`function_name [arg1 arg2 ......]` 或 第二种:function_name [arg1 arg2 ......] echo $? 这两种有什么区别呢? 举个例子来说: [[email protected] ~]# cat test.sh #!/bin/sh function aa

const修饰函数参数 const修饰函数返回值 const修饰成员函数

看到const 关键字,C++程序员首先想到的可能是const 常量.这可不是良好的条件反射.如果只知道用const 定义常量,那么相当于把火药仅用于制作鞭炮.const 更大的魅力是它可以修饰函数的参数.返回值,甚至函数的定义体. const 是constant 的缩写,"恒定不变"的意思.被const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性.所以很多C++程序设计书籍建议:"Use const whenever you need". 1

string 中的 length函数 和size函数 返回值问题

string 中的 length函数 和 size函数 的返回值  (  还有 char [ ] 中 测量字符串的  strlen 函数 ) 应该是 unsigned int 类型的 不可以 和 -1 比较. 应尽量避免 unsigned int 类型 和 int类型 数据 的比较 .当unsigned int 类型 和 int类型 数据 比较 时 ,会 把int 类型 转换 为 unsigned int类型 .如果 int是负数 ,转换 为 unsigned int 会是 一个 很大 的正整数

C语言的函数返回值

一:背景 谈到C语言的函数返回值,可能会感觉很亲切,不就是一个函数返回值嘛,当初学C语言的时候早就学过了很easy嘛,我曾经也是这么想的.后来要上研究生了,研究生阶段搞得就是C,所以又重新开始学习C,学习C的过程中遇到了很多问题,在此博客中一一记录.实际过程中遇到的第一个问题自然就是函数返回值了.如果有人问你在一个函数中声明一个字符串数组,最后再return这个数组.这可以实现嘛?如果是问我我可能会毫不犹豫的说OK.那事实呢?由此本文诞生了...... 二:问题 先看几个实际的例子: #incl

将引用作为函数返回值的优缺点

格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }好处:在内存中不产生被返回值的副本:(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的.因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!注意事项:(1)不能返回局部变量的引用.这条可以参照Effective C++[1]的Item 31.主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态.(2)不能返回函数内部new

linux编程中接收主函数返回值以及错误码提示

程序A创建子进程,并调用进程B,根据不调用的不同情况,最后显示结果不同. #include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <sys/types.h> #include <errno.h> int main() { pid_t pid, rpid; int stat; if ((pid = fork()) < 0) { perror("for

Linux Shell函数返回值

转:http://blog.csdn.net/ithomer/article/details/7954577 Shell函数返回值,一般有3种方式:return,argv,echo 1) return 语句shell函数的返回值,可以和其他语言的返回值一样,通过return语句返回.示例: [javascript] view plain copy print? #!/bin/bash - function mytest() { echo "arg1 = $1" if [ $1 = &q