为什么标准允许不返回编译的函数?

时间:2013-01-23 15:28:20

标签: c c99

f()即使它的签名说应该也不会返回。 为什么允许这个编译? C标准是否有理由不要求编译器使其失败?

我知道它是未定义的行为和所有,但为什么它首先被允许? 有历史原因吗?

double f(){}

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

4 个答案:

答案 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)

因为编译器无法判断函数是否在运行时返回。