f()即使它的签名说应该也不会返回。 为什么允许这个编译? C标准是否有理由不要求编译器使其失败?
我知道它是未定义的行为和所有,但为什么它首先被允许? 有历史原因吗?
double f(){}
int main()
{
f();
return 0;
}
答案 0 :(得分:4)
C标准是否需要编译器才能生成 它失败了?
通过调用未定义的行为,C标准允许编译器不那么复杂。确实存在某些情况,例如if
语句,很难说该函数是否返回值:
int f(int n)
{
if (n > 0) return 1;
}
f(5)
,编译器很容易说出来。{
功能是正确的。 f(-5)
,它也很容易被发现
未定义的返回值。但是,如果参数来自用户输入,例如,编译器应该如何知道函数是否返回值?由于这可能是一个有效或错误的程序,C标准允许编译器做他们想要的。 C旨在尽可能智能和简单。
答案 1 :(得分:1)
编译器可以当然分析该函数的所有代码路径,如果它不能证明函数总是返回有意义的值,则拒绝该程序。我认为标准没有强制要求的原因是,在过去,编译器的复杂程度远低于我们今天的工作。
当然使用这种函数的返回值是未定义的行为:
如果到达终止函数的},则为 函数调用由调用者使用,行为未定义。
答案 2 :(得分:1)
很容易判断您的函数将在不返回值的情况下到达最终。它不会返回和它不会调用任何可能阻止它到达终点的代码(如abort()
)。
实际上你的程序不在C99中有未定义的行为,因为调用者没有使用缺少的返回值。 6.9.1 / 12:
如果到达终止函数的},则为 函数调用由调用者使用,行为未定义。
所以你的代码风格有问题,但定义明确。
C ++标准更改了[diff.stat]
中有关该更改的规则和备注。它说该规则的C版本是支持在C不区分int
返回和void
返回的日子里写回的代码。因此,您的代码首先定义行为的原因是“遗留”。也就是说,AFAIK C总是区分double
返回和int
返回,所以它可能会使一个函数返回double
的UB掉到最后,如果已经完成了在合适的时间。
不管是否使用了返回值,请考虑一个tricker函数:
double f() {
if (g()) exit();
}
此函数也不包含return语句,但如果实际上g
总是返回true值或根本不返回,则不会到达函数的末尾。因此,即使使用其返回值,也应该接受此函数,在一般的C标准原则上,您应该知道您正在做什么并且意味着您所说的。如果g
在不同的TU中定义,那么您可能比编译器更了解它。
即使不是出于传统原因,我也很确定标准根本无法添加文本,以便定义需要检测的非返回场景编译器。这取决于实现的质量 - 如果可以在编译时确定您的函数不可能避免UB,那么编译器可能会发出警告,尽管不需要诊断。就此而言,当一般的C 实施者的原则定义行为时,它偶尔会发出警告,有些事情是如此愚蠢,没有任何用户可以合理地表达它们。
答案 3 :(得分:0)
因为编译器无法判断函数是否在运行时返回。