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变量没有正确构造,此时对其变量的访问可能会导致段错误。

为了避免这种语法陷阱,可采取以下的策略

  1. 有返回值函数,最后的返回语句不要放在if条件判断中,避免没有返回而编译器没有警告
  2. 编译时增加 -Wall 参数
  3. 对于自定义类,认真对待赋值构造函数,保证其正确构造和析构
  4. 非基础类型变量,最好不要作为函数返回的左值直接赋值构造
 string ret = foo();
 改为:
  string ret;
  ret = foo();
posted @ 2024-04-12 15:37  HL棣  阅读(17)  评论(0编辑  收藏  举报