自从我多年前意识到,默认情况下这不会产生错误(至少在 GCC 中),我一直想知道为什么?
我知道您可以发出编译器标志来产生警告,但它不应该总是错误吗?为什么非 void 函数不返回值是有效的?
评论中要求的示例:
#include <stdio.h>
int stringSize()
{
}
int main()
{
char cstring[5];
printf( "the last char is: %c\n", cstring[stringSize()-1] );
return 0;
}
…编译。
原文由 Catskul 发布,翻译遵循 CC BY-SA 4.0 许可协议
C99 和 C++ 标准要求非
void
函数返回值,但main
除外。将定义main
中缺少的 return 语句(返回0
)。在 C++ 中,如果执行实际上到达了非void
函数的结尾,而不是main
,则这是未定义的行为,而在 C 中,如果调用者 使用 返回值,则只有 UB。这意味着函数看起来可能会在没有返回值的情况下到达结尾,但实际上无法到达结束
}
。 John Kugelman 的回答 显示了一些示例,例如从if
的一侧调用的 noreturn 函数。如果执行实际上没有达到return
更早,这只是未定义的行为。基本原理包括检查每个真实的代码路径是否返回一个值是非常困难的(不知道哪些函数永远不会返回),所以像你的例子那样 编译 一个函数并不违法,只是像你的main
那样实际调用它——---
确实。作为扩展,至少有一个编译器 (MSVC) 允许使用 inline assembly 设置返回值,但大多数其他编译器仍然需要在使用 inline
asm
的函数中使用 return 语句。来自 C++11 草案:
§ 6.6.3⁄2
§ 3.6.1⁄5
int foo() { int a = 5; int b = a + 1; }
int main() { std::cout << foo() << std::endl; } // may print 6
”`
此代码具有形式上未定义的行为,实际上它依赖于 调用约定 和 体系结构。在具有特定编译器的特定系统上,如果禁用优化,则返回值是最后一次表达式评估的结果,存储在该系统处理器的
eax
寄存器中。这似乎是 GCC 内部禁用优化的结果,因为在这种情况下,如果它需要任何返回值寄存器来实现语句, 它就会选择返回值寄存器。在 C++ 模式下启用优化后,GCC 和 clang 假定此执行路径不可访问,因为它包含未定义的行为。他们甚至不发出
ret
指令,因此执行落入 .text 部分中的下一个函数。当然,未定义的行为意味着任何事情都可能发生。