今天在review以前的代码的时候,遇到了一个比较奇怪的现象,函数的有返回值,但只在if后面有return,else后面忘写了。但这个版本的代码已经调试验证通过了,没有问题的,这就很怪异。

考验一道题

下面这道题Print的内容是什么?

# include <iostream>class Test {public:Test (int xx, std::string yy) {x = xx;y = yy;}int x;std::string y;void Print() {std::cout << "x : " << x << " y : " << y << std::endl;}
};Test fun(bool flag) {Test t (0, "0");if (flag) {t.x = 1;t.y = "1";return t;} else {t.x = -1;t.y = "-1";}
}int main(int argc, char const *argv[])
{Test t = fun(false);t.Print();return 0;
}

编译并运行:

g++ test.cpp -o test
./test

可能你也能猜到最终的结果:

x : -1 y : -1

也可能会疑问,在fun函数的else语句中并没有提供返回值啊?为什么还能有输出么?或者,会问函数的return语句不全,不是应该报错么?

函数的返回值

没有return语句
如果一个函数需要有返回值,但是没有return语句,这会出现什么?

# include <iostream>class Test {public:Test (int xx, std::string yy) {x = xx;y = yy;}int x;std::string y;void Print() {std::cout << "x : " << x << " y : " << y << std::endl;}
};Test fun(bool flag) {Test t (0, "0");if (flag) {t.x = 1;t.y = "1";} else {t.x = -1;t.y = "-1";}
}int main(int argc, char const *argv[])
{Test t = fun(false);t.Print();return 0;
}

编译并运行:

g++ test.cpp -o test
./test

编译没有任何问题,但是在运行的时候出现了错误:

x : 2 y : H��H9�u�H�[]A\A]A^A_Ðf.���H�H��x :  y : 01-1;d����h����^���h���������U����j�������� <���Hh��������@zRx�����*zRx�$
*** Error in `./test': munmap_chunk(): invalid pointer: 0x000000000040112d ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f4648daf7e5]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x1a8)[0x7f4648dbc698]
./test[0x4010d0]
./test[0x400f56]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f4648d58830]
./test[0x400d09]
======= Memory map: ========
00400000-00402000 r-xp 00000000 08:01 263039                             /home/yngzmiao/test/test/test
00601000-00602000 r--p 00001000 08:01 263039                             /home/yngzmiao/test/test/test
00602000-00603000 rw-p 00002000 08:01 263039                             /home/yngzmiao/test/test/test
01656000-01688000 rw-p 00000000 00:00 0                                  [heap]
7f4648a2f000-7f4648b37000 r-xp 00000000 08:01 5247874                    /lib/x86_64-linux-gnu/libm-2.23.so
7f4648b37000-7f4648d36000 ---p 00108000 08:01 5247874                    /lib/x86_64-linux-gnu/libm-2.23.so
7f4648d36000-7f4648d37000 r--p 00107000 08:01 5247874                    /lib/x86_64-linux-gnu/libm-2.23.so
7f4648d37000-7f4648d38000 rw-p 00108000 08:01 5247874                    /lib/x86_64-linux-gnu/libm-2.23.so
7f4648d38000-7f4648ef8000 r-xp 00000000 08:01 5247804                    /lib/x86_64-linux-gnu/libc-2.23.so
7f4648ef8000-7f46490f8000 ---p 001c0000 08:01 5247804                    /lib/x86_64-linux-gnu/libc-2.23.so
7f46490f8000-7f46490fc000 r--p 001c0000 08:01 5247804                    /lib/x86_64-linux-gnu/libc-2.23.so
7f46490fc000-7f46490fe000 rw-p 001c4000 08:01 5247804                    /lib/x86_64-linux-gnu/libc-2.23.so
7f46490fe000-7f4649102000 rw-p 00000000 00:00 0
7f4649102000-7f4649118000 r-xp 00000000 08:01 5247842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f4649118000-7f4649317000 ---p 00016000 08:01 5247842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f4649317000-7f4649318000 rw-p 00015000 08:01 5247842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f4649318000-7f464948a000 r-xp 00000000 08:01 2099340                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f464948a000-7f464968a000 ---p 00172000 08:01 2099340                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f464968a000-7f4649694000 r--p 00172000 08:01 2099340                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f4649694000-7f4649696000 rw-p 0017c000 08:01 2099340                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f4649696000-7f464969a000 rw-p 00000000 00:00 0
7f464969a000-7f46496c0000 r-xp 00000000 08:01 5247776                    /lib/x86_64-linux-gnu/ld-2.23.so
7f464989d000-7f46498a3000 rw-p 00000000 00:00 0
7f46498be000-7f46498bf000 rw-p 00000000 00:00 0
7f46498bf000-7f46498c0000 r--p 00025000 08:01 5247776                    /lib/x86_64-linux-gnu/ld-2.23.so
7f46498c0000-7f46498c1000 rw-p 00026000 08:01 5247776                    /lib/x86_64-linux-gnu/ld-2.23.so
7f46498c1000-7f46498c2000 rw-p 00000000 00:00 0
7ffc906a9000-7ffc906ca000 rw-p 00000000 00:00 0                          [stack]
7ffc90796000-7ffc90799000 r--p 00000000 00:00 0                          [vvar]
7ffc90799000-7ffc9079b000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
已放弃 (核心已转储)

也就是说,如果一个函数声明或定义了有返回值,那么就必须要有return语句!

那么,如果出现了分支语句,必须保证所有的出口都必须有return么?也就是本文开始提出的那个问题。当然,答案也已经被证明出来了,并不需要。

非全出口return语句

在正式解释之前,再看一个例子:

# include <iostream>class Test {public:Test (int xx, std::string yy) {x = xx;y = yy;}int x;std::string y;void Print() {std::cout << "x : " << x << " y : " << y << std::endl;}
};Test fun(bool flag) {if (flag) {Test t (0, "0");t.x = 1;t.y = "1";return t;} else {Test t (0, "0");t.x = -1;t.y = "-1";}
}int main(int argc, char const *argv[])
{Test t = fun(false);t.Print();return 0;
}

编译并运行:

g++ test.cpp -o test
./test

编译没有任何问题,但是在运行的时候出现了错误:

x : 2 y : H��H9�u�H�[]A\A]A^A_Ðf.���H�H��x :  y : 01-1;l����h����^����^���������"���8�������(
���P*���ph��� ����hzRx�����*zRx*** Error in `./test': munmap_chunk(): invalid pointer: 0x00000000004012ad ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f4ad9fd07e5]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x1a8)[0x7f4ad9fdd698]
./test[0x40121e]
./test[0x4010a3]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f4ad9f79830]
./test[0x400d89]
======= Memory map: ========
00400000-00402000 r-xp 00000000 08:01 262629                             /home/yngzmiao/test/test/test
00601000-00602000 r--p 00001000 08:01 262629                             /home/yngzmiao/test/test/test
00602000-00603000 rw-p 00002000 08:01 262629                             /home/yngzmiao/test/test/test
011d1000-01203000 rw-p 00000000 00:00 0                                  [heap]
7f4ad9c50000-7f4ad9d58000 r-xp 00000000 08:01 5247874                    /lib/x86_64-linux-gnu/libm-2.23.so
7f4ad9d58000-7f4ad9f57000 ---p 00108000 08:01 5247874                    /lib/x86_64-linux-gnu/libm-2.23.so
7f4ad9f57000-7f4ad9f58000 r--p 00107000 08:01 5247874                    /lib/x86_64-linux-gnu/libm-2.23.so
7f4ad9f58000-7f4ad9f59000 rw-p 00108000 08:01 5247874                    /lib/x86_64-linux-gnu/libm-2.23.so
7f4ad9f59000-7f4ada119000 r-xp 00000000 08:01 5247804                    /lib/x86_64-linux-gnu/libc-2.23.so
7f4ada119000-7f4ada319000 ---p 001c0000 08:01 5247804                    /lib/x86_64-linux-gnu/libc-2.23.so
7f4ada319000-7f4ada31d000 r--p 001c0000 08:01 5247804                    /lib/x86_64-linux-gnu/libc-2.23.so
7f4ada31d000-7f4ada31f000 rw-p 001c4000 08:01 5247804                    /lib/x86_64-linux-gnu/libc-2.23.so
7f4ada31f000-7f4ada323000 rw-p 00000000 00:00 0
7f4ada323000-7f4ada339000 r-xp 00000000 08:01 5247842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f4ada339000-7f4ada538000 ---p 00016000 08:01 5247842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f4ada538000-7f4ada539000 rw-p 00015000 08:01 5247842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f4ada539000-7f4ada6ab000 r-xp 00000000 08:01 2099340                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f4ada6ab000-7f4ada8ab000 ---p 00172000 08:01 2099340                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f4ada8ab000-7f4ada8b5000 r--p 00172000 08:01 2099340                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f4ada8b5000-7f4ada8b7000 rw-p 0017c000 08:01 2099340                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f4ada8b7000-7f4ada8bb000 rw-p 00000000 00:00 0
7f4ada8bb000-7f4ada8e1000 r-xp 00000000 08:01 5247776                    /lib/x86_64-linux-gnu/ld-2.23.so
7f4adaabe000-7f4adaac4000 rw-p 00000000 00:00 0
7f4adaadf000-7f4adaae0000 rw-p 00000000 00:00 0
7f4adaae0000-7f4adaae1000 r--p 00025000 08:01 5247776                    /lib/x86_64-linux-gnu/ld-2.23.so
7f4adaae1000-7f4adaae2000 rw-p 00026000 08:01 5247776                    /lib/x86_64-linux-gnu/ld-2.23.so
7f4adaae2000-7f4adaae3000 rw-p 00000000 00:00 0
7ffeaa7a8000-7ffeaa7c9000 rw-p 00000000 00:00 0                          [stack]
7ffeaa7fa000-7ffeaa7fd000 r--p 00000000 00:00 0                          [vvar]
7ffeaa7fd000-7ffeaa7ff000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
已放弃 (核心已转储)

和第二例没有return的语句一样,也出现了类似的错误。但这段程序是有return语句的啊!

细细分析代码的区别,其实可以发现,分支中没有return分支的函数出口,好像有一个默认的返回值,就是有return语句分支的返回值变量的所在地址!

第一例中,else语句尽管没有return语句,但是返回了一个与if语句中return的同一个变量地址的内容;而第三例中,else语句中并没有if语句中return的同一变量地址的内容(栈被释放了)!

当然,这些都是通过区分代码得到的浅显的认知,但这究竟是什么原因呢?

编译器的锅(RVO)

一般来说,为了从一个函数得到运行结果,常规的途径有两个:通过返回值和通过传入函数的引用或指针。

当通过返回值的时候,如果是类的对象或指针的时候,需要注意拷贝构造函数!

拷贝构造函数通常有三种使用场景:

用一个对象初始化另一个对象;
函数的形参与实参结合时;
函数返回时,栈上对象复制到返回值中时。
也就是说,当函数返回时,需要将栈上的对象通过拷贝构造函数,复制到调用该函数的返回值中去。即,把return t的t复制到Test t = fun(false)的t中去。

而RVO(C++的返回值优化)是指:C++标准允许一种(编译器)实现省略创建一个只是为了初始化另一个同类型对象的临时对象。基本手段是直接将返回的对象构造在调用者栈帧上,这样调用者就可以直接访问这个对象而不必复制。指定这个参数(-fno-elide-constructors)将关闭这种优化,强制G++在所有情况下调用拷贝构造函数。

现在就清楚了,当函数有一个return后,就会将该return的对象直接构造在调用者栈上,就不需要走return的拷贝构造函数。

当然可以试验一下,利用-fno-elide-constructors关闭优化:

编译并运行:

g++ -fno-elide-constructors test.cpp -o test
./test

编译没有任何问题,但是在运行的时候出现了错误:

*** Error in `./test': munmap_chunk(): invalid pointer: 0x00000000004012bd ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fdcb3df47e5]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x1a8)[0x7fdcb3e01698]
./test[0x401228]
./test[0x40105d]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fdcb3d9d830]
./test[0x400d89]
======= Memory map: ========
00400000-00402000 r-xp 00000000 08:01 262629                             /home/yngzmiao/test/test/test
00601000-00602000 r--p 00001000 08:01 262629                             /home/yngzmiao/test/test/test
00602000-00603000 rw-p 00002000 08:01 262629                             /home/yngzmiao/test/test/test
00f88000-00fba000 rw-p 00000000 00:00 0                                  [heap]
7fdcb3a74000-7fdcb3b7c000 r-xp 00000000 08:01 5247874                    /lib/x86_64-linux-gnu/libm-2.23.so
7fdcb3b7c000-7fdcb3d7b000 ---p 00108000 08:01 5247874                    /lib/x86_64-linux-gnu/libm-2.23.so
7fdcb3d7b000-7fdcb3d7c000 r--p 00107000 08:01 5247874                    /lib/x86_64-linux-gnu/libm-2.23.so
7fdcb3d7c000-7fdcb3d7d000 rw-p 00108000 08:01 5247874                    /lib/x86_64-linux-gnu/libm-2.23.so
7fdcb3d7d000-7fdcb3f3d000 r-xp 00000000 08:01 5247804                    /lib/x86_64-linux-gnu/libc-2.23.so
7fdcb3f3d000-7fdcb413d000 ---p 001c0000 08:01 5247804                    /lib/x86_64-linux-gnu/libc-2.23.so
7fdcb413d000-7fdcb4141000 r--p 001c0000 08:01 5247804                    /lib/x86_64-linux-gnu/libc-2.23.so
7fdcb4141000-7fdcb4143000 rw-p 001c4000 08:01 5247804                    /lib/x86_64-linux-gnu/libc-2.23.so
7fdcb4143000-7fdcb4147000 rw-p 00000000 00:00 0
7fdcb4147000-7fdcb415d000 r-xp 00000000 08:01 5247842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7fdcb415d000-7fdcb435c000 ---p 00016000 08:01 5247842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7fdcb435c000-7fdcb435d000 rw-p 00015000 08:01 5247842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7fdcb435d000-7fdcb44cf000 r-xp 00000000 08:01 2099340                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fdcb44cf000-7fdcb46cf000 ---p 00172000 08:01 2099340                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fdcb46cf000-7fdcb46d9000 r--p 00172000 08:01 2099340                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fdcb46d9000-7fdcb46db000 rw-p 0017c000 08:01 2099340                    /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fdcb46db000-7fdcb46df000 rw-p 00000000 00:00 0
7fdcb46df000-7fdcb4705000 r-xp 00000000 08:01 5247776                    /lib/x86_64-linux-gnu/ld-2.23.so
7fdcb48e2000-7fdcb48e8000 rw-p 00000000 00:00 0
7fdcb4903000-7fdcb4904000 rw-p 00000000 00:00 0
7fdcb4904000-7fdcb4905000 r--p 00025000 08:01 5247776                    /lib/x86_64-linux-gnu/ld-2.23.so
7fdcb4905000-7fdcb4906000 rw-p 00026000 08:01 5247776                    /lib/x86_64-linux-gnu/ld-2.23.so
7fdcb4906000-7fdcb4907000 rw-p 00000000 00:00 0
7ffe40205000-7ffe40226000 rw-p 00000000 00:00 0                          [stack]
7ffe40320000-7ffe40323000 r--p 00000000 00:00 0                          [vvar]
7ffe40323000-7ffe40325000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
已放弃 (核心已转储)

也就是说,确实是因为RVO(C++的返回值优化)的原因。

当然,保证每个分支出口都有return才是最重要的!

相关阅读
【C++踩坑】说说g++的-fno-elide-constructors参数
C++的返回值优化(RVO,Return Value Optimization)

C++ 函数需要有返回值,但非全分支return(RVO)相关推荐

  1. 【C++】C++函数需要有返回值,但非全分支return(RVO)

    今天在review以前的代码的时候,遇到了一个比较奇怪的现象,函数的有返回值,但只在if后面有return,else后面忘写了.但这个版本的代码已经调试验证通过了,没有问题的,这就很怪异. 考验一道题 ...

  2. wpf 窗口的返回值_如何:获取页函数的返回值

    如何:获取页函数的返回值How to: Get the Return Value of a Page Function 03/30/2017 本文内容 本示例显示如何获取页函数的返回值.This ex ...

  3. 决定c++语言中函数的返回值类型的是,全国2009年10月高等教育自学考试C++程序设计试题及部分参考答案...

    全国2009年10月高等教育自学考试 C++程序设计试题 课程代码:04737 一.单项选择题(本大题共20小题,每小题1分,共20分) 在每小题列出的四个备选项中只有一个是符合题目要求的,请将其代码 ...

  4. 一木.溪桥学Python-09:函数的返回值、函数的作用域、作用域的优先级、递归函数、内置文件中常用方法、匿名函数lambda、高阶函数

    一木.溪桥 在Logic Education跟Amy学Python 12期:Python基础课 一木.溪桥学Python-09:函数的返回值.作用域.作用域的优先级.递归函数.内置文件中常用方法.匿名 ...

  5. 站长在线Python精讲:Python中函数的返回值

    欢迎你来到站长在线的站长学堂学习Python知识,本文学习的是<Python中函数的返回值>.本文的主要内容有:函数的返回值的含义.函数设置返回值的作用.return语句位置与多条 ret ...

  6. 非阻塞模式下 SEND 和 RECV 函数的返回值总结

    send 和 recv 函数的各种返回值意义: 返回值 n 返回值含义 大于 0 成功发送 n 个字节 0 对端关闭连接 小于 0( -1) 出错或者被信号中断或者对端 TCP 窗口太小数据发不出去( ...

  7. 【C 语言】字符串模型 ( 两头堵模型 | 将 两头堵模型 抽象成业务模块函数 | 形参返回值 | 函数返回值 | 形参指针判空 | 形参返回值操作 )

    文章目录 一.将 两头堵模型 抽象成业务模块函数 二.完整代码示例 一.将 两头堵模型 抽象成业务模块函数 将 两头堵模型 抽象成业务模块函数 相关要点 : 形参返回值 : 函数的返回值 , 一般使用 ...

  8. python函数的返回值是返回引用吗_python-函数(上):函数返回值、函数调用、前向引用...

    编程方法: 1.面向对象:类--class 2.面向过程:过程--def 3.函数式编程:函数--def #python中函数#函数的定义#打印一个佛祖镇楼 -> 一个功能点的解释 defpri ...

  9. if python 判断函数返回值_Python函数的返回值和作用域

    函数的返回值和作用域 1.返回值 def guess(x): if x > 3: return "> 3" else: return "<= 3&quo ...

  10. python 函数递归一次增加一次变量_python基础之函数、返回值,局部变量、全局变量,递归(继续补充不定长参数)...

    1.python中函数定义:函数是逻辑结构化和过程化的一种编程方法.(完成某一种特定的功能) def test02(): #"" msg = 'hello WuDaLang' re ...

最新文章

  1. SQL Mon 介绍
  2. Python 实现整数线性规划:分枝定界法(Branch and Bound)
  3. volatile关键字解析~高级java必问
  4. android 时间戳 时区,三句话理解时区与时间戳
  5. 数据科学中的简单线性回归
  6. Moodle插件开发笔记
  7. 人工智能/数据科学比赛汇总 2019.5
  8. 【kafka】服务器上Kafka启动 Cannot allocate memory
  9. python---之打印日志logging
  10. 简单java程序_简单的Java程序
  11. swift项目第七天:构建访客界面以及监听按钮点击
  12. 简单聊聊智能硬件的固件测试
  13. Unity笔记-29-ARPG游戏项目-01-第三人称相机
  14. 如何可以快速解决网络劫持
  15. Zabbix-agent在Windows下安装报[8576]:ERROR:cannot connect to Service Manager:[0x00000005]错误的解决...
  16. 区块链赋能医疗产业报告
  17. 腾讯滑块vData_VMP分析
  18. 微信扫描二维码,跳转到第三方网页,并获取用户OpenId和基本信息
  19. Paper Reading 《SimCSE》
  20. 利用html2canvas截取div保存为png图片并下载

热门文章

  1. NetScaler Active-Active模式
  2. Hybrid 开发:JsBridge - Web 和客户端的桥
  3. 【linux高级程序设计】(第十一章)System V进程间通信 4
  4. oracle 9i、10g、11g、12c官方文档
  5. 搭建cacti 期间问题总结
  6. 迫切想要成功之后的喜悦感,失败太久有点心灵上小小的打击,还需要继续前进。...
  7. 14.UNIX 环境高级编程--高级IO
  8. 循序渐进之Spring AOP(2) - 基本概念
  9. 《数学之美》—统计语言模型
  10. [2019杭电多校第四场][hdu6623]Minimal Power of Prime