如果一个函数返回一个有效的返回类型但没有返回任何值,编译器是否可以返回垃圾值?

16
如果一个函数的返回类型不是void,但函数没有返回任何值,那么我猜编译器会返回一个垃圾值(可能被视为未初始化的值)。这是在编译时发生的,那么为什么不应该显示错误呢?
例如,
int func1() {
    return; // error
}

int func2() {
    // does not return anything
}
第二个func2应该会抛出一个错误,但它没有。有什么原因吗?我的想法是,可以把它看作是未初始化的值,所以如果我们需要在第二种情况下抛出错误,那么我们需要抛出错误,如果一个值没有被初始化,就像这样。
  int i;  // error
  int i = 6;  // okay
任何想法,还是这个问题是重复的?感谢您的帮助。

2
这是C还是C++?你是否收到编译器错误或警告?你正在使用哪个编译器? - littleadv
可能是重复的问题:gcc选项:在没有返回语句的非void函数上发出警告 - Oliver Charlesworth
我看到了警告,问题有点像,为什么没有错误?为什么编译器没有抛出错误,尽管看到了警告? - howtechstuffworks
返回值。编译错误会被显示或打印,但不会被抛出。异常会被抛出。不要误用标准术语。 - user207421
4个回答

23
在C++中,下面的代码行为是未定义的:

[stmt.return]/2 ... 函数结束时没有返回值的语句流等同于没有返回值的return语句; 在有返回值的函数中,这会导致未定义的行为。...

大多数编译器会对类似问题中的代码产生警告。

C++标准不要求将其视为编译时错误,因为在一般情况下很难正确地确定代码是否真正流出函数末尾,或者函数是否通过异常(或longjmp或类似机制)退出。

考虑以下示例代码:

int func3() {
    func4();
}
如果func4()抛出异常,那么这段代码是完全没问题的。编译器可能无法看到func4()的定义(因为分离编译),因此无法知道它是否会抛出异常。
此外,即使编译器可以证明func4()不会抛出异常,它仍然必须证明func3()在被调用之前才能合法地拒绝程序。这种分析需要检查整个程序,这与分离编译不兼容,甚至在一般情况下也不可能实现。

3
许多编程语言(包括C#和Java)在上面的示例中需要一个return someValue;语句,即使它永远不会被执行。语言设计涉及到权衡,即有用的诊断信息相对于偶尔需要编写愚蠢代码以取悦编译器的相对价值。我不同意许多Java/C#的决定,但这个特定的问题我没有异议。 - supercat
1
@supercat:我的回答的意思是:“C++使得这种行为未定义;但不幸的是,编译器并不能拒绝这段代码,因为在编译时无法(完美地)检测到实际的未定义行为是否发生。”。这并不意味着设计一种不同的语言来保守地拒绝那些可能有效但不符合该语言类型系统安全模型的程序是不可能的(对于其他事情,C++已经做到了)。 - Mankarse

12
在C语言中,引用N1256 6.9.1p12的话:
如果到达终止函数的},并且调用者使用函数调用的值,则行为是未定义的。
因此,对于非void函数而言,不返回任何值是合法的(但不好的做法),但如果它这样做,并且调用者尝试使用结果,则行为是未定义的。请注意,它不一定只返回一些随意的值;就标准而言,任何事情都有可能发生。
在ANSI C之前,没有void关键字,因此编写不返回值的函数的方法是省略返回类型,使其隐式返回int。在有返回值的函数中要求使用return语句会破坏旧代码。这也需要编译器进行额外的分析,以确定所有代码路径是否都命中了return语句; 对于现代编译器来说这种分析是合理的,但当C首次标准化时可能过于繁重。
C ++稍微严格一些。在C ++中:

超出函数范围的流动等价于一个没有值的return;这会导致值返回函数的未定义行为。

因此,调用者尝试使用(不存在的)结果与否的行为是未定义的。
C和C ++编译器当然可以警告缺少return语句或控制路径,在函数末尾没有执行return语句,但是各自的标准不要求这样做。

2
在简单情况下,这样的分析是合理的 - 在复杂情况下,编译器可能甚至没有足够的信息来知道它是否总是会执行return语句。 - caf
可能要求所有可能的控制路径执行return语句;我认为C#是这样做的。例如,{ if (1) { return 42; } else { puts("No return here"); }即使实际上无法避免return,也会违反这种要求。而C和C++则不会这样做。 - Keith Thompson
@Keith:这是不可行的,请参考Mankarse的答案。 - Lightness Races in Orbit
@KeithThompson:为什么C++在这方面很严格?是因为它们是由不同委员会标准化的不同语言,还是有其他技术原因? - Destructor
1
@PravasiMeet:C语言必须保持与ANSI之前的程序兼容。而C++的设计则没有同样的限制。 - Keith Thompson

6
在C语言中,非void函数在结束时不返回值实际上是合法的,只要调用代码没有尝试使用返回值。另一方面,一个没有表达式的return语句在非void函数中是不允许出现的。相关的C99标准如下:第一种情况是§6.9.1:

如果到达终止函数的},并且函数调用的值被调用者使用,则行为未定义。

第二种情况是§6.8.6.4:

return语句没有表达式只能出现在返回类型为void的函数中。


1
@KeithThompson: 这就是为什么我用“在C语言中…”开头的原因。 - caf
不确定我是怎么错过了那个问题。(但是该问题被标记为C和C++。) - Keith Thompson

2

你的两个函数都是不规范的。它们之间的区别在于,你的func1违反了有关return语句使用规则的规定,而你的func2是未定义行为。在func1中的return语句是非法的,实现必须诊断此问题。在func2中缺少return语句是未定义的行为。大多数编译器会诊断此问题,但没有一个必须这样做。


在C语言中,第一个函数并不是非法的。只有当调用者实际尝试使用返回值时,行为才是未定义的。 - caf
是的,我同意,这更像是一个测试,试图找出为什么C委员会要这样做... - howtechstuffworks

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