【问题标题】:If a function returns no value, with a valid return type, is it okay to for the compiler to return garbage?如果一个函数没有返回值,但返回类型有效,编译器是否可以返回垃圾?
【发布时间】:2012-04-13 17:27:44
【问题描述】:

如果一个函数的返回类型不是void,并且该函数没有返回任何内容,那么我猜编译器会返回一个垃圾值(可能被视为未初始化的值)。它发生在编译时,为什么不应该显示错误?

例如,

int func1() {
    return; // error
}

int func2() {
    // does not return anything
}

第二个func2 应该抛出一个错误,但它不会。有什么原因吗?我的想法是,它可以看作是一个未初始化的值,所以如果我们需要在第二种情况下抛出错误,那么我们需要抛出错误,如果一个值未初始化,比如说

  int i;  // error
  int i = 6;  // okay

任何想法,或者这是一个重复的问题?感谢您的帮助。

【问题讨论】:

  • 是 C 还是 C++?您是否收到编译器错误或警告?你用的是哪个编译器?
  • 好吧,我看到了警告,问题有点像,为什么没有错误?为什么编译器虽然看到了却不抛出错误?
  • 返回值。显示或打印编译错误。他们都没有被抛出。抛出异常。不要滥用标准术语。

标签: c++ c return-value initialization garbage


【解决方案1】:

在 C++ 中,此类代码具有未定义的行为:

[stmt.return]/2 ... 从函数末尾流出相当于没有值的返回;这会导致值返回函数中的未定义行为。 ...

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

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

考虑

int func3() {
    func4();
}

如果func4() 抛出,那么这段代码完全没问题。编译器可能看不到func4()的定义(因为单独编译),所以不知道会不会抛出。

此外,即使编译器可以证明func4() 没有抛出异常,它仍然必须证明func3() 确实被调用,然后才能合法地拒绝程序。这种分析需要对整个程序进行检查,这与单独编译是不兼容的,在一般情况下甚至是不可能的。

【讨论】:

  • 包括 C# 和 Java 在内的许多语言在上面的示例中都需要 return someValue; 语句,即使它永远无法执行。语言设计是关于权衡,以及有用的诊断与偶尔需要愚蠢的代码来安抚编译器的相对价值。我不同意 Java/C# 的许多决定,但我对这个特别的决定没有疑虑。
  • @supercat:我的回答应该被解释为:“C++ 产生了这种未定义的行为;但不幸的是,这还不足以让编译器真正拒绝这段代码,因为这是不可能的(在编译时) 来(完美地)检测是否发生了实际的未定义行为。”。这并不是说不可能设计一种不同的语言来保守地拒绝可能有效但不符合该语言类型系统安全模型的程序(C++ 已经这样做了) , 对于其他事情)。
【解决方案2】:

在 C 中,引用 N1256 6.9.1p12:

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

因此,非 void 函数无法返回值是合法的(但不是一个好主意),但如果这样做并且调用者尝试使用结果,则行为未定义.请注意,它不一定只返回一些任意值;就标准而言,一切皆有可能。

Pre-ANSI C 没有 void 关键字,因此编写不返回值的函数的方法是省略返回类型,使其隐式返回 int。在返回值的函数中要求 return 语句会破坏旧代码。它还需要编译器进行额外分析,以确定所有代码路径都命中了return 语句;这种分析对于现代编译器来说是合理的,但在 C 首次标准化时可能是一个过重的负担。

C++ 稍微严格一些。在 C++ 中:

从函数的末尾流出相当于一个 return 没有价值;这会导致值返回中的未定义行为 功能。

所以无论调用者是否尝试使用(不存在的)结果,行为都是不确定的。

C 和 C++ 编译器当然可以警告缺少 return 语句,或在未执行 return 语句的情况下从函数末尾脱落的控制路径,但各自的标准没有要求他们这样做。

【讨论】:

  • 这样的分析只在简单的情况下是合理的——在复杂的情况下,编译器甚至可能没有足够的信息来知道它总是会命中return语句。
  • 可以要求所有可能的控制路径都执行return 语句;我认为 C# 就是这样做的。例如,{ if (1) { return 42; } else { puts("No return here"); } 会违反这样的要求,即使实际上不可能避免 return。 C 和 C++ 只是不这样做。
  • @Keith:这是不可行的。请参阅 Mankarse 的回答。
  • @KeithThompson:为什么 C++ 对此很严格?是因为两者都是由不同委员会标准化的不同语言,还是因为这背后的任何其他技术原因?
  • @PravasiMeet:C 必须保持对 ANSI 前程序的兼容性。 C++ 的设计没有同样的限制。
【解决方案3】:

在 C 中,非 void 函数在没有返回值的情况下结束实际上是合法的,只要调用代码不尝试使用返回值

另一方面,不带表达式的return 语句不允许出现在非空函数中。

对于第一种情况,C99 标准的相关部分是 §6.9.1:

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

对于第二种情况,§6.8.6.4:

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

【讨论】:

  • @KeithThompson:这就是我以“In C...”开头的原因
  • 不知道我是怎么错过的。 (但这个问题同时被标记为 C 和 C++。)
【解决方案4】:

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

【讨论】:

  • 在 C 中,第一个函数不是格式错误的。仅当调用者实际尝试使用返回值时,该行为才是未定义的。
  • 是的,我同意,这更像是一个测试,并试图找出 C 委员会为什么会有这种方式......
猜你喜欢
  • 1970-01-01
  • 2014-08-24
  • 1970-01-01
  • 2023-02-06
  • 2013-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多