在C89(也称为ANSI C)中省略return语句是否未定义行为?

15
考虑以下基本示例:
#include <stdio.h>

int main(void)
{
    printf("Hi there!\n");
}

我需要处理一下这段关于C89的问题吗?我尝试从这个问题中获得一些信息,但是最受欢迎的答案声称它是实现定义的,在这里绝对没有UB(伴随着Keith Thompson的评论,这看起来是矛盾的)。
在§3.16 Definitions and conventions中,规范表示:
如果在约束条件之外出现了“必须”或“不得”的要求,则行为将是未定义的。通过单词“未定义行为”或省略任何明确的行为定义,在本国际标准中显式地指示未定义行为。它们之间没有强调上的区别:它们都描述了“未定义的行为”。
和§5.1.2.2.3 Program termination
调用main函数后的返回值等同于使用main函数返回的值作为参数调用exit函数。如果main函数执行了一个没有指定返回值的返回语句,则返回给宿主环境的终止状态是未定义的。
我的理解是,后面的子句并不包括缺少返回值的情况,因为return语句从未被调用,因此前面的子句适用。
然而进一步阅读表明情况有所不同,§6.6.6.4 返回语句
如果执行了一个没有表达式的return语句,并且函数调用的值由调用者使用,则行为是未定义的。到达终止函数的}相当于执行一个没有表达式的return语句。
现在适用于5.1.2.2.3子句。
如果main函数执行了一个没有指定返回值的return语句,则返回给宿主环境的终止状态是未定义的。
术语“终止状态未定义”似乎不是UB,也不是任何特定行为,而更像是超出C标准范围的内容,更像是“让宿主环境自己担心,我们从这里开始洗手”。这个解释正确吗?

1
如果你还没有看到,这里有一些有趣的答案:https://dev59.com/7HVC5IYBdhLWcg3wtzkQ - Rizier123
1
我的首选C89草案(http://port70.net/~nsz/c/c89/c89-draft.html)与你提供的引用完全不符。章节编号完全不同,相应的文本也不同。当然,这只是一个草案,而不是最终标准。尽管如此,如果我们不在标准上达成一致,我们不可能就一个答案达成一致。 - John Bollinger
2
我会读取它的意思是相当于调用没有表达式的return,这样根据§5.1.2.2.3所述,没有表达式的return会导致给主机OS返回一个未定义的返回代码。你是正确的,这不是未定义行为,因为发生的事情是已知的并且总是相同的;程序将终止并将一个值作为返回代码返回给OS。 未定义的是那个值将是什么。所以,是的,我会说你的解释是正确的。 - aruisdante
这在C99中是被允许的。虽然这并没有回答你关于C89的问题,但我认为从编译器编写者的角度来看,在C89中触发UB而在C99中定义良好的行为是很疯狂的。这甚至可能被认为是C89的一个缺陷。 - Rufflewind
从您提供的标准摘录来看,“返回给主机环境的终止状态未定义”是隐含的,因此到达 main() 的尾部}会导致程序终止。因此,这一点似乎是已定义的。然而,我倾向于说,在这种情况下是否调用 exit() 是不确定的,如果调用 exit(),那么其参数的值肯定是未定义的。这些细节属于 C 标准的范围,因此该程序具有未定义行为。 - John Bollinger
2个回答

4
多年前,我曾经因为这个问题而进行了调试。如果你的代码路径有些有返回值,而另一些没有,则情况会更加有趣。
如评论中所猜测的那样,所展示的行为确实是“未定义”的,但仅未定义的部分是返回的值(它不像许多其他可能导致程序崩溃的“未定义”情况)。
这些天,这实际上已经构成了安全风险,因为通常返回“未定义”值的是CPU寄存器中正常用于返回值的内容(或者在某些实现中是在堆栈上),这理论上可以被用来泄露敏感数据。

你是在特指 main 函数,还是普通函数? - Rufflewind
如果我没记错的话,应该是 main,但那是在90年代初。那已经是很久以前的事了。 - James

2
我相信标准委员会的意图是,这个终止状态值是未指定的。
根据N739草案:
引用如下: 如果/main/函数执行了没有指定返回值的返回语句,则返回给主机环境的终止状态是未指定,请将子条款5.1.2.2.3的最后一句改为:

如果/main/函数执行了没有指定返回值的返回语句,则返回给主机环境的终止状态是未指定

[在其他地方小心避免未定义值的概念。]

你可能还会发现,在进一步的标准中,术语已经改变,连续使用了“未指定”一词。
我想说,它绝对不是故意的未定义行为,但仍然无法确定它是未指定行为还是像我所说的根本没有行为。另一个表明后者的摘录是§1 范围:
这个国际标准没有规定:
  • C程序被数据处理系统调用的机制;
我认为这也意味着这个国际标准没有规定程序在主机环境中如何处理终止状态。

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