【发布时间】:2015-11-18 14:28:15
【问题描述】:
通读 C99 标准后,我找不到任何禁止在下面定义函数 f 的部分:
struct s { double d; };
int f() {
if (0) return (struct s){.d = 3.14};
// There is intentionally no return statement of type int, which is valid
}
int main() {
f();
return 0;
}
特别是,应该定义这个程序的行为,因为返回值是 (a) 从来没有达到过,并且 (b) 从来没有使用过,即使达到了。
不过,我的大多数编译器(GCC、Clang 和 CompCert)都阻止了该程序的编译,并出现错误 returning 'struct s' from a function with incompatible result type 'int'。我确实设法用tcc 编译它,但我不知道这是否主要是由于运气(即缺乏验证)。
有人可以确认这个程序在语法上是否 C99 有效,并且它的行为是完全明确定义的,或者以其他方式指出标准禁止它的地方吗?
我实际上更喜欢我的编译器拒绝它,但是例如一些宏定义可能会产生类似于这个的代码,所以如果它们是有效的,那么接受这些程序实际上是有用的。
背景
以下是我可以找到的 C99 标准的可能相关摘录,以及我对为什么它们不应该禁止我的程序在语法上有效和语义上定义良好的理由:
§6.8.6.4返回声明
§6.8.6.4.1 带有表达式的 return 语句不得出现在返回类型为 void 的函数中。没有表达式的 return 语句只能出现在返回类型为 void 的函数中。
在我的代码中,我们有一个非void 函数和一个非void 返回,所以一切看起来都很好。
§6.8.6.4.3 如果执行带有表达式的return 语句,则表达式的值作为函数调用表达式的值返回给调用者。如果表达式的类型与它所在的函数的返回类型不同,则该值将被转换为好像通过赋值给具有该函数的返回类型的对象一样。
因为return语句从不执行,所以上述不适用。
§6.9.1函数定义
§6.9.1.12如果到达终止函数的},并且调用者使用了函数调用的值,则行为未定义。
函数调用的结果没有被调用者使用,所以应该定义行为。
【问题讨论】:
-
一般来说,您不应期望编译器接受
if (always_false) { invalid_code; },因为编译器可能无法说服自己always_false始终为假。即使可以,它的类型检查阶段也不应该依赖于这种类型的分析。 -
对于
int f()函数,if (0) return (struct s){.d = 3.14};和没有返回值的最终}实际上是 2 个独立的问题,最好是 2 个帖子。无论如何,深思熟虑的问题。 -
在 ubuntu linux 14.04 上,使用 gcc,带参数:
-Wall -Wextra -pedantic -std=c99编译器输出两条消息:1) ...4:`0: 当返回类型 'struct s' 但 ' 时错误类型不兼容int'是预期的。 2)...6:1:警告:控制到达非无效函数的结尾[-Wreturn-type]所以发布的代码有两个主要问题。任何现代编译器(尤其是如果您使用与 c99 兼容的编译器)都会告诉您同样的情况。当编译器说代码错误时,你可以相信。 'turbo c' 是一个古老的编译器,适合它的时代,但不是现在 -
对 c99 标准的分析,因为它与发布的代码有关,是有缺陷的。让我们记住,发布的代码返回的是结构,而不是双精度。通常,不能将结构分配给 int。如果代码实际上将双精度分配给 int,那么发布代码的那部分将是有效的。
标签: c language-lawyer c99