C++陷阱—指定的返回类型的函数实际没有返回时会发生什么
当一个string变量作为左值接收函数返回,当函数没有正确返回时,该string变量被如何构造?
请看如下代码:
#include <iostream>
#include <string.h>
using namespace std;
string foo()
{
if(0)
{
return "you get";
}
}
int main(int argc, char **argv)
{
string ret = foo();
if (ret == "")
{
cout << "ret == \"\"\n";
}
if (ret.empty())
{
cout << "ret is empty\n";
}
cout << "main end\n";
return 0;
}
输出:
main end
Segmentation fault (core dumped)
这里的变量ret
接收函数foo()
的返回,但函数条件未全覆盖导致没有返回。
它的运行结果没有输出ret == ""
,也没有输出ret is empty
, 也就是ret 并不是ret == ""
,也不是empty
,并且发生了段错误。
错误原因猜测
这个段错误导致的原因是什么?
很明显string ret = foo()
这里的ret变量调用的是一个重载赋值符的构造函数,这里会涉及到一个内存的拷贝。所以是对非法内存的拷贝引起的吗?还是Main函数析构时释放了一个非法的空间?
因为“main end” 已经打印出来了,所以该段错误应该是发生在析构时期,该段错误可以猜测为是对变量ret的析构导致,也就是ret没有正确构造使其指向了非法内存,这里只是猜测,因为手上暂时没有源码,猜测有待求证!!
该段代码编译时,我们会期待其输出警告“函数没有正确返回”之类,但实际没有警告,即使有警告,也不知道这里竟会发生段错误,这就是C++的缺陷与语法陷阱之一。
为了避免这种问题,编译时可以增加-Wall参数,输出所有警告。
举一反三
那么其他类型的返回值也会段错误吗?
像int等基础类型,不会导致段错误,会根据其构造原则返回随机值或者固定值。
其具体原因还有待深究!
#include <iostream>
#include <string.h>
using namespace std;
int foo(int i)
{
if(0 == i)
{
return 0;
}
}
int main(int argc, char **argv)
{
int ret = foo(0);
int ret2 = foo(100);
cout << "ret == "<<ret<<endl;
cout << "ret2 == "<<ret2<<endl;
return 0;
}
输出:
ret == 0
ret2 == 0
总结:
当一个string变量作为左值接收函数返回,当函数没有正确返回时,该string变量没有正确构造,此时对其变量的访问可能会导致段错误。
为了避免这种语法陷阱,可采取以下的策略
- 有返回值函数,最后的返回语句不要放在if条件判断中,避免没有返回而编译器没有警告
- 编译时增加 -Wall 参数
- 对于自定义类,认真对待赋值构造函数,保证其正确构造和析构
- 非基础类型变量,最好不要作为函数返回的左值直接赋值构造
string ret = foo();
改为:
string ret;
ret = foo();