为什么在没有返回值的情况下stream出非void函数的结尾不会产生编译器错误?
自从我多年前意识到,这不会默认产生一个错误,(至less在gcc中)我总是想知道为什么?
我明白,你可以发出编译器标志来产生警告,但不应该总是一个错误? 为什么没有返回值是有效的非void函数是有意义的?
评论中所要求的一个例子:
#include <stdio.h> int stringSize() { } int main() { char cstring[5]; printf( "the last char is: %c\n", cstring[stringSize()-1] ); return 0; }
…编译。
C99和C ++标准不要求函数返回一个值。 只有在main
函数中定义了返回值函数中缺less的返回语句(返回0
)。
其基本原理包括检查每个代码path是否返回一个值是非常困难的,并且可以使用embedded式汇编程序或其他棘手的方法设置返回值。
从C ++ 11草案:
§6.6.3 / 2
在函数的结尾处stream动会导致返回值函数中出现未定义的行为。
§3.6.1 / 5
如果控制到达
main
的结尾而没有遇到return
语句,效果就是执行的效果return 0;
请注意,在C ++ 6.6.3 / 2中描述的行为在C中是不一样的
如果用-Wreturn-type选项调用gcc,gcc会给你一个警告。
-Wreturn-type当函数定义为默认为int的返回types时发出警告。 还要警告任何返回值没有返回值的函数返回types不是无效的(从函数体的末尾落下被认为没有值返回),以及关于一个函数中的expression式的返回语句返回types是无效的。
此警告由-Wall启用。
就好像一个好奇心,看看这个代码的作用:
#include <iostream> int foo() { int a = 5; int b = a + 1; } int main() { std::cout << foo() << std::endl; } // may print 6
这段代码具有正式的未定义的行为,实际上它是调用约定和架构的依赖。 在一个特定的系统中,使用一个特定的编译器,返回值是上一次expression式求值的结果,存储在该系统处理器的eax
寄存器中。
gcc不会默认检查所有代码path是否返回一个值,因为一般情况下这是不能完成的。 它假定你知道你在做什么。 考虑一个使用枚举的常见示例:
Color getColor(Suit suit) { switch (suit) { case HEARTS: case DIAMONDS: return RED; case SPADES: case CLUBS: return BLACK; } // Error, no return? }
程序员知道,除了一个bug,这个方法总是返回一个颜色。 海湾合作委员会相信,你知道你在做什么,所以它不会强制你把函数的底部返回。
另一方面,javac试图validation所有的代码path返回一个值,如果它不能certificate他们都这么做,就会抛出一个错误。 这个错误是由Java语言规范强制的。 请注意,有时它是错误的,你必须把一个不必要的返回语句。
char getChoice() { int ch = read(); if (ch == -1 || ch == 'q') { System.exit(0); } else { return (char) ch; } // Cannot reach here, but still an error. }
这是一个哲学的差异。 C和C ++比Java或C#更宽容和信任语言,因此新语言中的一些错误是C / C ++中的警告,并且一些警告在默认情况下被忽略或closures。
你的意思是,为什么从一个返回值的函数(即没有显式return
情况下退出)的末尾stream失不是一个错误?
首先,在C中,函数是否返回有意义的内容只有在执行代码实际使用返回值时才是关键。 也许这个语言并不想强迫你在任何时候知道你不打算使用它。
其次,语言规范显然不希望强制编译器作者检测和validation所有可能的控制path,以确定是否存在明确的return
(虽然在很多情况下这并不难)。 另外,一些控制path可能导致非返回函数 – 编译器通常不知道的特征。 这样的path可能成为恼人的误报来源。
还要注意,C和C ++在这种情况下的行为定义有所不同。 在C ++中,只要返回一个返回值函数的末尾就是未定义的行为(不pipe函数的结果是否被调用代码使用)。 在C中,只有在调用代码尝试使用返回值时,才会导致未定义的行为。
在C / C ++中,从声明返回的函数不返回是合法的。 有很多用例,例如调用exit(-1)
,或调用它的函数或抛出exception。
编译器不会拒绝合法的C ++,即使它会导致UB,如果你不问。 尤其是,您不要求产生警告 。 (海湾合作委员会仍然打开一些默认情况下,但是当添加这些似乎与新functionalignment而不是新的警告旧function)
更改默认的no-arg gcc发出一些警告可能是现有脚本的重大改变或使系统。 精心devise的人 – -Wall
和处理警告,或切换个别警告。
学习使用C ++工具链是学习成为C ++程序员的障碍,但C ++工具链通常是由专家编写的。
在什么情况下不会产生错误? 如果它声明了一个返回types,并且不返回任何东西,这听起来像是一个错误。
我能想到的一个例外是main()
函数,它根本不需要return
语句(至less在C ++中,我没有任何一个C标准)。 如果没有返回,就会return 0;
是最后一个陈述。
我相信这是因为遗留代码(C从来不需要C语言的return语句)。 依靠这个“function”,可能有巨大的代码库。 但是至less在许多编译器(包括gcc和clang)上有-Werror=return-type
标志。
听起来你需要打开你的编译器警告:
$ gcc -Wall -Wextra -Werror -xc - int main(void) { return; } cc1: warnings being treated as errors <stdin>: In function 'main': <stdin>:1: warning: 'return' with no value, in function returning non-void <stdin>:1: warning: control reaches end of non-void function $
在c99中违反了约束条件,但在c89中没有。 对比:
C89:
3.6.6.4
return
语句约束
带有expression式的
return
语句不应出现在返回types为void
的函数中。
C99:
6.8.6.4
return
语句约束
带有expression式的
return
语句不应出现在返回types为void
的函数中。 没有expression式的return
语句只能出现在返回types为void
的函数中。
即使在--std=c99
模式下,gcc也只会发出警告(虽然不需要启用额外的-W
标志,如默认或c89 / 90所要求的那样)。
编辑在c89中添加,到达终止函数的“等同于执行没有expression式的return
语句”(3.6.6.4)。 但是在c99中,行为是不确定的(6.9.1)。