考虑以下程序,这显然是错误的:
#include <cstdio>
double test(int n) {
if (n % 2 == 0)
return 0.0;
// warning: control reaches end of non-void function
}
int main() {
printf("%.9lf\n", test(0));
printf("%.9lf\n", test(1));
printf("%.9lf\n", test(2));
printf("%.9lf\n", test(3));
return 0;
}
在32位Ubuntu 8.04上使用g ++ 4.2.4版(Ubuntu 4.2.4-1ubuntu4)编译时,会产生以下输出:
0.000000000
nan
0.000000000
nan
在64位Ubuntu 10.04上使用g ++ 4.4.3版(Ubuntu 4.4.3-4ubuntu5)编译时,会产生以下输出:
0.000000000
0.000000000
0.000000000
0.000000000
似乎较旧的编译器为了返回NaN而不是垃圾而做了一些额外的工作,而较新的编译器只返回内存中的任何内容。究竟是什么导致了这种行为差异以及如何控制它并使其在不同的编译器版本中可预测?
编辑:很抱歉没有提及之前的未定义行为。我知道差异来自于这个程序有未定义的行为。我想知道为什么以前的gcc版本似乎付出了一些努力并产生了一致返回NaN的代码和 这种行为改变为后者中观察到的行为gcc版本。另外,通过“可预测”,我的意思不是如何用C ++编写好的程序,而是如何控制这个gcc行为(可能有一些命令行选项?)。
答案 0 :(得分:12)
您显示的代码未定义行为,因为未能从所有控制路径返回test
中的值。
除了首先编写正确的程序之外,没有什么可以期待的,也无法控制它。
编辑:我认为你不应该依赖编译器似乎做什么。
即使你认为应该,也不应该。
查看dissasembly将揭示这段特定代码将被编译成什么,但除了这段特定代码之外,您将无法概括为其他任何内容,尤其是在存在优化的情况下。未定义的行为真的意味着您的想法:除非您碰巧掌握GCC的代码库,否则无法保证您观察到的内容在其他环境中可以重现。
唯一明智的解决方案是使用-Wall进行编译并修复那些“并非所有返回路径返回值”,以便行为1)定义,2)由定义您的。
答案 1 :(得分:2)
当double
没有返回任何内容时,您的函数会返回n % 2 != 0
。
你处在未定义行为的土地上。
答案 2 :(得分:1)
我想知道为什么是前者 gcc版似乎付出了一些努力 并生成一致的代码 返回NaN和此行为时 改为观察到的那个 后者的gcc版本。另外,通过 “可预测的”我的意思不是如何写 C ++中的好程序,但是如何 控制这个gcc行为(也许用 一些命令行选项?)。
因为您处于未定义行为的范围内,所以为什么的可能性极小。编译器编写者(理所当然地)倾向于不在编译器在未定义行为的情况下做出任何努力。无论发生什么,几乎可以肯定是编译器版本之间(显然)无关的变化的紧急行为,而不是任何编译器工程师的慎重选择。改变它不会有一面旗帜。不要依赖它。修复你的代码。
答案 3 :(得分:0)
忽略警告你的危险。
if (n % 2 == 0) return 0.0; else return 0.0;
正常工作,否则,只有一半的情况test()
返回垃圾。
未定义的行为与版本无关,因为我也获得了NaN:
$ gcc --version
gcc (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2
$ uname -a
Linux tallguy 2.6.38-8-generic #42-Ubuntu SMP Mon Apr 11 03:31:50 UTC 2011 i686 i686 i386 GNU/Linux