自从我多年前意识到这一点,默认情况下这不会产生错误(至少在GCC中),我一直想知道为什么?
我知道您可以发出编译器标志来产生警告,但是它不应该总是出错吗?为什么非void函数没有返回值才有效?
评论中要求的示例:
#include <stdio.h>
int stringSize()
{
}
int main()
{
char cstring[5];
printf( "the last char is: %c\n", cstring[stringSize()-1] );
return 0;
}
Run Code Online (Sandbox Code Playgroud)
...编译.
fni*_*eto 144
C99和C++标准不需要函数来返回值.值返回函数中缺少的return语句将0仅在main函数中定义(返回).
基本原理包括检查每个代码路径是否返回一个值是非常困难的,并且可以使用嵌入式汇编程序或其他棘手的方法设置返回值.
来自C++ 11草案:
§6.6.3/ 2
流出函数的末尾[...]会导致值返回函数中的未定义行为.
§3.6.1/ 5
如果控件在
main没有遇到return语句的情况下到达结尾,则效果就是执行Run Code Online (Sandbox Code Playgroud)return 0;
请注意,C++ 6.6.3/2中描述的行为在C中是不同的.
如果用-Wreturn-type选项调用它,gcc会给你一个警告.
-Wreturn-type每当使用默认为int的返回类型定义函数时发出警告.还要警告任何return语句没有返回值,返回类型不是void的函数(从函数体的末尾掉下来被认为是没有值返回),以及一个函数中带有表达式的return语句return-type无效.
-Wall启用此警告.
就像好奇心一样,看看这段代码的作用:
#include <iostream>
int foo() {
int a = 5;
int b = a + 1;
}
int main() { std::cout << foo() << std::endl; } // may print 6
Run Code Online (Sandbox Code Playgroud)
此代码具有正式的未定义行为,并且在实践中它依赖于约定和体系结构.在一个特定的系统上,使用一个特定的编译器,返回值是最后一个表达式求值的结果,存储在该eax系统的处理器的寄存器中.
Joh*_*ica 42
默认情况下,gcc不会检查所有代码路径是否返回值,因为通常无法执行此操作.它假设你知道你在做什么.考虑使用枚举的常见示例:
Color getColor(Suit suit) {
switch (suit) {
case HEARTS: case DIAMONDS: return RED;
case SPADES: case CLUBS: return BLACK;
}
// Error, no return?
}
Run Code Online (Sandbox Code Playgroud)
程序员知道,除非有bug,否则此方法总是会返回一种颜色.gcc相信你知道你在做什么,所以它不会强迫你在函数的底部放回一个.
另一方面,javac尝试验证所有代码路径都返回一个值,如果它不能证明它们都有效,则会抛出错误.Java语言规范强制要求此错误.请注意,有时它是错误的,你必须输入一个不必要的return语句.
char getChoice() {
int ch = read();
if (ch == -1 || ch == 'q') {
System.exit(0);
}
else {
return (char) ch;
}
// Cannot reach here, but still an error.
}
Run Code Online (Sandbox Code Playgroud)
这是一个哲学上的差异.C和C++是比Java或C#更宽松和更信任的语言,因此较新语言中的一些错误是C/C++中的警告,默认情况下会忽略或关闭某些警告.
AnT*_*AnT 13
你的意思是,为什么流出一个值返回函数的末尾(即没有显式退出return)不是错误?
首先,在C中,当执行代码实际使用返回值时,函数是否返回有意义的内容才是关键.也许当你知道大多数时候你不会使用它时,语言不想强迫你返回任何东西.
其次,显然语言规范不希望强制编译器作者检测和验证存在显式的所有可能的控制路径return(尽管在许多情况下这并不难).此外,一些控制路径可能会导致非返回函数 - 编译器通常不知道的特征.这样的路径可能成为恼人的误报的来源.
另请注意,C和C++在这种情况下对行为的定义不同.在C++中,只是在值返回函数的末尾流出始终是未定义的行为(无论调用代码是否使用了函数的结果).在C中,仅当调用代码尝试使用返回的值时,才会导致未定义的行为.
C 和 C++ 有不同的规则。
C 中的语言规则是,如果达到}返回非值的函数的结束并且void调用者尝试使用该值,则行为是未定义的。只要调用者不使用该值,从函数末尾脱落就具有明确定义的行为。
可能需要所有可能的控制路径来执行return在离开函数之前执行语句,但 C 传统上并不要求编译器进行此类代码分析。(许多编译器无论如何都会进行这种分析,并在适当的情况下发出警告。)
允许从非功能的末端脱落的主要原因void是历史性的。K&R C(Kernighan 和 Ritchie 的书 1978 年第一版中描述的版本,在 1989 年 ANSI 和 1990 年 ISO C 标准之前)没有关键字void或类型。在 1999 年 ISO C 标准之前,C 具有“隐式int”规则,这意味着您可以声明或定义一个没有显式返回类型的函数,并且它将返回一个int结果。
在 K&R C 中,如果您想要一个不返回结果的函数,您可以在没有显式返回类型的情况下定义它,并且简单地不返回值:
#include <stdio.h>
do_something() {
printf("Not returning a value\n");
}
int main() {
do_something();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
该函数实际上会返回一些垃圾int值,调用者会悄悄地忽略这些值。
在现代 C 语言中,你可以这样写:
#include <stdio.h>
void do_something(void) {
printf("Not returning a value\n");
}
int main(void) {
do_something();
}
Run Code Online (Sandbox Code Playgroud)
这保证了调用者不能尝试使用返回值。从 C89/C90 开始,该语言仍然支持旧样式以避免破坏现有代码。int当C99 中删除隐式规则时,对非void函数无法返回值的要求没有改变(并且大多数 C99 及更高版本的编译器仍然int默认支持隐式规则,可能会带有警告,因此旧的 K&R C 代码仍然可以进行编译)。
在 C++ 中,从构造函数、析构函数、函数以外的函数末尾流出void,或main会导致未定义的行为,无论调用者尝试对结果执行什么操作。
在C / C ++中,合法的做法是不要从声称要返回某些内容的函数中返回。有许多用例,例如调用exit(-1),或调用它或引发异常的函数。
即使您要求不这样做,编译器也不会拒绝合法的C ++,即使它导致了UB。特别是,您不要求生成任何警告。(默认情况下,Gcc仍会启用某些功能,但是添加后,这些功能似乎与新功能保持一致,而不是针对旧功能的新警告)
更改默认的no-arg gcc以发出一些警告可能是对现有脚本或make系统的重大更改。精心设计的-Wall警告或者处理警告,或者切换单个警告。
学习使用C ++工具链是学习成为C ++程序员的障碍,但是C ++工具链通常由专家编写并为专家编写。
| 归档时间: |
|
| 查看次数: |
44245 次 |
| 最近记录: |