Erratic behaviour with missing return in c++ and optimizations
假设您在 c 中编写了一个函数,但心不在焉地忘记输入单词
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#include <iostream> #include <vector> using namespace std; double max_1(double n1, int max_2(const int n1, size_t max_length(const vector<int>& v1, int main(void) cout << max_2(3,4) << endl; cout << max_length(vector<int>(3,1),vector<int>(4,1)) << endl; return 0; |
这是我在不同优化级别编译它时得到的结果:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
$ rm ./a.out; g++ –O0 ./test.cpp && ./a.out
nan nan 134525024 134525024 4 4 $ rm ./a.out; g++ –O1 ./test.cpp && ./a.out 0 0 0 0 0 0 $ rm ./a.out; g++ –O2 ./test.cpp && ./a.out 0 0 0 0 0 0 $ rm ./a.out; g++ –O3 ./test.cpp && ./a.out 0 0 0 0 0 0 |
现在假设您正在尝试调试函数 max_length。在生产模式下你会得到错误的答案,所以你在调试模式下重新编译,现在当你运行它时一切正常。
我知道有一些方法可以通过添加适当的警告标志 (
为什么编译器甚至同意编译一个没有返回语句的函数?旧代码是否需要此功能?
为什么输出取决于优化级别?
这是丢弃值返回函数末尾的未定义行为,这在草案 C 标准部分 `6.6.31 的 return 语句中有所介绍:
Flowing off the end of a function is equivalent to a return with no
value; this results in undefined behavior in a value-returning
function.
编译器不需要发出诊断,我们可以从
The set of diagnosable rules consists of all syntactic and semantic
rules in this International Standard except for those rules containing
an explicit notation that a€?no diagnostic is requireda€? or which are
described as resulting in a€?undefined behavior.a€?
尽管编译器通常会尝试捕获各种未定义的行为并产生警告,尽管通常您需要使用正确的标志集。对于
-Wall -Wextra -Wconversion -pedantic
一般来说,我鼓励您使用
将警告转化为错误
编译器因在优化阶段利用未定义行为而臭名昭著,请参阅通过查找死代码查找未定义行为错误以获取一些很好的示例,包括在处理此代码时臭名昭著的 Linux 内核空指针检查删除:
1
2 3 |
struct foo *s = …;
int x = s–>f; if (!s) return ERROR; |
由于未定义的行为是不可预测的,因此在更激进的设置下,编译器在许多情况下会进行更激进的优化,其中许多可能没有太大的直观意义,但是,嘿,这是未定义的行为,所以无论如何你都不应该有任何期望。 请注意,尽管在很多情况下编译器可以确定函数在一般情况下没有正确返回,但这是暂停问题。在运行时自动执行此操作会产生违反不为您不使用的理念付费的成本。虽然 您可能想在此处查看此答案 唯一的原因是编译器允许你没有 return 语句,因为可能有许多不同的执行路径,确保每个都以 return 退出在编译时可能会很棘手,所以编译器会处理它给你。 要记住的事情: 如果 main 结束时没有返回,它将总是返回 0。
如果另一个函数没有返回就结束它总是返回eax寄存器中的最后一个值,通常是最后一条语句
优化会更改汇编级别的代码。这就是为什么你会出现奇怪的行为,编译器正在为你”修复”你的代码,当执行事情时会改变你的代码,给出不同的最后一个值,从而返回值。
希望这有帮助!
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/268722.html