我遇到了g++(版本10.2.1)的一个(对我来说)意外行为。这里是一个最简示例:
我知道代码并没有太多意义,因为如果 x 不等于 0,f 函数并不会返回任何值。当我使用
我明白了 - 编译器看到唯一可能的返回值在if语句内部,所以它移除了if语句。然而,在我的实际(非最小化)代码中,我花了几个小时才明白这一点:我的函数f还有一个else分支,但为了测试新功能,我将其注释掉了,结果出现了奇怪的行为。
我的问题是,这是否是一种合理的优化,还是应该被视为编译器的错误?
编辑:谢谢大家快速回答。是的,正如我所说,我也收到了警告。我同意应该认真对待警告。但是,为了提供一个更接近我实际情况的例子:我甚至在调用f的函数中忽略了返回值 - 函数的主要“输出”是通过引用传递的,实际输出只是额外的值。所以我的(错误)想法是,如果代码返回一个无意义的值,我不需要它(再次强调,仅用于测试目的),这并不重要。我仍然觉得奇怪的是,如果我将下面的示例中的"int f.."替换为"void f",并将"return 5"替换为"return",它的行为就会改变。
最终编辑:感谢讨论。总结一下(如果以后有人看到这个):我原本认为,如果函数末尾缺少一个返回值,那么只是函数的返回值未定义(在扩展示例中可能还可以接受)。但实际上,整个函数的行为都是未定义的,这远不止于此。
#include <iostream>
int f(int x) {
if(x==0) {
std::cout << "I am here" << std::endl;
return 5;
}
}
int main(int argc, char** argv) {
int x = f(atoi(argv[1]));
std::cout << x << std::endl;
return 0;
}
我知道代码并没有太多意义,因为如果 x 不等于 0,f 函数并不会返回任何值。当我使用
g++ -Wall -O3 -DNDBUG -o bla bla.cpp
编译时,编译器也会自然地对此发出警告。如果我无论如何运行程序,将会发生以下情况:$ ./bla 0
I am here
5
$ ./bla 1
I am here
5
$ ./bla 2
I am here
5
$ ./bla 3
I am here
5
我明白了 - 编译器看到唯一可能的返回值在if语句内部,所以它移除了if语句。然而,在我的实际(非最小化)代码中,我花了几个小时才明白这一点:我的函数f还有一个else分支,但为了测试新功能,我将其注释掉了,结果出现了奇怪的行为。
我的问题是,这是否是一种合理的优化,还是应该被视为编译器的错误?
编辑:谢谢大家快速回答。是的,正如我所说,我也收到了警告。我同意应该认真对待警告。但是,为了提供一个更接近我实际情况的例子:我甚至在调用f的函数中忽略了返回值 - 函数的主要“输出”是通过引用传递的,实际输出只是额外的值。所以我的(错误)想法是,如果代码返回一个无意义的值,我不需要它(再次强调,仅用于测试目的),这并不重要。我仍然觉得奇怪的是,如果我将下面的示例中的"int f.."替换为"void f",并将"return 5"替换为"return",它的行为就会改变。
#include <iostream>
int f(int x,int& z) {
if(x==0) {
std::cout << "I am here" << std::endl;
z=7;
return 5;
}
}
int main(int argc, char** argv) {
int z=1;
f(atoi(argv[1]),z);
std::cout << z << std::endl;
return 0;
}
输出
$ ./bla 0
I am here
7
$ ./bla 1
I am here
7
$ ./bla 2
I am here
7
$ ./bla 3
I am here
7
最终编辑:感谢讨论。总结一下(如果以后有人看到这个):我原本认为,如果函数末尾缺少一个返回值,那么只是函数的返回值未定义(在扩展示例中可能还可以接受)。但实际上,整个函数的行为都是未定义的,这远不止于此。
f
承诺 返回一个int
值。但是在函数中有一些路径并没有这样做,导致了上述的未定义行为。如果编译器还没有对此发出警告,请启用更多的警告。并将警告视为错误。这绝对不是编译器的错误,如果你的代码存在未定义行为,那么编译器基本上可以随意处理。 - Some programmer dudex==0
为真”,更有帮助的是认为“当x==0
为假时,编译器可以自由决定要做任何该死的事情”。如果x==0
为假,你未能从一个有返回值的函数中返回一个值,因此行为是未定义的。编译器可以选择任何行为。它选择了“如果x==0
为真,我将产生相同的行为”。 - Nathan Piersonx==0
为真”,更有帮助的是认为“当x==0
为假时,编译器可以自由地做任何它想做的事情”。如果x==0
为假,你未能从一个有返回值的函数中返回一个值,因此行为是未定义的。编译器可以选择任何行为。它选择了“如果x==0
为真,我将产生相同的行为”。 - undefined