C++是否强制要求使用return语句?

8

我发现了一个关于我的C++编译器的小怪异。

我有一段不算太复杂的代码需要重构,但是我意外地忘记在某个路径中添加了一个返回语句。我的错。然而这段代码还是编译通过了,当程序执行到那个路径时就会出现segmentation fault错误。

我的问题是:这是编译器的bug吗?还是说C++编译器没有强制要求非void类型的函数必须包含一个return语句?

哦,并且需要明确的是,在这种情况下,它只是一个没有伴随else的无用if语句。没有跳转、退出或者中止的操作。


3
如果使用gcc,请使用-Wreturn-type选项,可能还需要加上-Werror= - Georg Fritzsche
同样的事情也发生在我的gcc 4.4上。 - Peter G.
3个回答

13

个人认为这应该是一个错误:

int f() {
}

int main() {
    int n = f();
    return 0;
}

但大多数编译器将其视为警告,您甚至可能需要使用编译器开关才能获得该警告。例如,在g ++上,您需要使用-Wall才能获得:

[neilb@GONERIL NeilB]$ g++ -Wall nr.cpp
nr.cpp: In function 'int f()':
nr.cpp:2: warning: no return statement in function returning non-void

当然,使用g++编译时,无论如何都应该至少加上-Wall参数。


13

不能保证C++编译器会执行此操作。一个C++函数可能通过编译器未知的机制跳出其控制流程。使用 C++ 编写操作系统内核时的上下文切换就是一个例子。由被调用函数抛出但调用者可能无法访问代码的未捕获异常也是其中之一。

有些其他语言,如 Java,在编译时明确强制要求所有路径都返回一个值。在 C++ 中,这并不是真实的情况,就像访问数组越界一样,许多其他情况在语言中也没有得到检查。


1
@Johannes 好的,还有abort()和terminate()。但是编译器无法看到它们是否否定了返回的需要,除非以某种方式将它们视为“特殊”。 - anon
1
@Neil 没错。这就是为什么它不能强制你返回一个值,因为它不知道它们是否否定了返回的需要,就像你完美地注意到的那样。它假设它不会返回并信任你。 - Johannes Schaub - litb
1
@litb:天哪,为什么?它通常会在代码中的不可能路径上执行不变量,为什么不能强制返回一个值,即使这个代码永远不会被使用?请参见http://ideone.com/MHDKl - jpalecek
@Neil,我完全同意你的观点。在我自己的编程语言中,我也会强制执行这一点。但似乎C和C++是不关心这个问题的编程语言。 - Johannes Schaub - litb
@Johannes 在你的注意力在这个有点相关的问题上,你看到这个问题了吗?https://dev59.com/Q3A75IYBdhLWcg3wqK10 - 你对信号如何影响对象生命周期有什么看法吗? - anon
显示剩余9条评论

4
编译器不会强制执行此项规定,因为您知道编译器不知道的实际可能路径。编译器通常只知道特定文件,而不知道其他可能影响任何给定函数内部流程的文件。所以这不是一个错误。
然而,在Visual Studio中,这是一个警告。我们应该注意所有警告...对吧? :)
编辑:关于何时会发生这种情况存在一些讨论。这里有一个来自我的个人代码库的修改后但真实的示例;
enum TriBool { Yes, No, Maybe };

TriBool GetResult(int input) {
    if (TestOne(input)) {
        return Yes;
    } else if (TestTwo(input)) {
        return No;
    }
}

请见谅,因为这是旧代码。原来有一个“else return maybe”的语句在其中。如果TestOne和TestTwo在不同的编译单元中,则编译器在遇到此代码时无法确定对于给定的输入,TestOne和TestTwo是否都可能返回false。作为编写TestOne和TestTwo的程序员,您知道如果TestOne失败,则TestTwo将成功。也许这些测试有副作用,所以必须进行测试。没有“else if”会更好吗?也许吧。很可能是。但是重点是这是合法的C ++,编译器无法知道是否可能退出而没有返回语句。我同意,这很丑陋并且不是好的编码方式,但它是合法的,Visual Studio会给出警告但仍然可以编译。

请记住,C ++并不是为了保护你免受伤害。它是关于让你按照语言的限制做你想做的事情,即使包括自己踩坑。


这没有任何意义。编译器必须知道哪些路径是可能的,才能编译代码。 - anon
1
@Neil 我认为他说得有道理。编译器并不总是在编译时就知道所有情况。就像你在发表评论时不知道我会回答一样。但现在你知道了,可能会采取预防措施来回答,或者不回答。或者你会像C++一样感到完全惊讶,并做出未定义的事情 :) - Johannes Schaub - litb
1
在我看来,那是无稽之谈。编译器在分析代码后,任何给定函数的控制流图都是完全已知的。可能情况是,在执行代码时有些边缘永远不会被遍历(考虑断言),但编译器会将其视为这样,并且它所引起的只是一个(过度)保守的猜测。 - jpalecek
编译器是否应该测试代码的所有可能输入?实际上,在编译时,它无法知道所有“路径”中哪些可以实际采取,哪些不能。因此,有些情况下,它无法识别以无返回结果结束的“路径”?不过,我仍然需要想象出这些情况是什么。 - ShinTakezou
-1: 我同意@jpalecek和匿名用户的观点。这是无意义的:编译器总是可以确定代码是否缺少return语句。 这实际上就是gcc使用“-Wreturn-type”的方式。 另一方面,它无法弄清楚永远不会到达的路径。 @JohannesSchaub-litb:编译器只在编译时工作,链接器在所有材料都是机器码时不会处理代码流程。 - qdii

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接