gpt4 book ai didi

c++ - 在 C++ 和优化中缺少返回值的不稳定行为

转载 作者:太空狗 更新时间:2023-10-29 23:50:51 25 4
gpt4 key购买 nike

假设您用 C++ 编写了一个函数,但心不在焉地忘记输入单词 return。在那种情况下会发生什么?我希望编译器会提示,或者至少在程序到达那个点时会引发段错误。然而,实际发生的事情要糟糕得多:程序会喷出垃圾。不仅如此,实际输出还取决于优化级别!下面是一些演示此问题的代码:

#include <iostream>
#include <vector>

using namespace std;

double max_1(double n1,
double n2)
{
if(n1>n2)
n1;
else
n2;
}

int max_2(const int n1,
const int n2)
{
if(n1>n2)
n1;
else
n2;
}

size_t max_length(const vector<int>& v1,
const vector<int>& v2)
{
if(v1.size()>v2.size())
v1.size();
else
v2.size();
}

int main(void)
{
cout << max_1(3,4) << endl;
cout << max_1(4,3) << endl;

cout << max_2(3,4) << endl;
cout << max_2(4,3) << endl;

cout << max_length(vector<int>(3,1),vector<int>(4,1)) << endl;
cout << max_length(vector<int>(4,1),vector<int>(3,1)) << endl;

return 0;
}

这是我在不同优化级别编译它时得到的结果:

$ 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。在生产模式下你得到错误的答案,所以你在 Debug模式下重新编译,现在当你运行它时一切正常。

我知道有一些方法可以通过添加适当的警告标志 (-Wreturn-type) 来完全避免这种情况,但我仍然有两个问题

  1. 为什么编译器甚至同意编译一个没有 return 语句的函数?遗留代码需要此功能吗?

  2. 为什么输出取决于优化级别?

最佳答案

这是 undefined behavior丢弃值返回函数的末尾,这在 draft C++ standard 中有介绍`6.6.31 返回语句部分说:

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.

编译器不需要发出诊断,我们可以从 1.4 Implementation compliance 部分看到这一点,它说:

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 “no diagnostic is required” or which are described as resulting in “undefined behavior.”

尽管编译器通常会 try catch 范围广泛的未定义行为并产生警告,但通常您需要使用正确的标志集。对于 gccclang,我发现以下一组标志很有用:

-Wall -Wextra -Wconversion -pedantic

一般来说,我会鼓励您使用 -Werror 将警告转化为错误。

编译器因在优化阶段利用未定义行为而臭名昭著,请参阅 Finding Undefined Behavior Bugs by Finding Dead Code对于一些很好的例子,包括处理这段代码时臭名昭著的 Linux 内核空指针检查删除:

struct foo *s = ...;
int x = s->f;
if (!s) return ERROR;

gcc 推断,由于 ss->f; 中被取消引用,并且因为取消引用空指针是未定义的行为,所以 s 不能为空,因此优化了下一行的 if (!s) 检查(从我的 answer here 复制)。

由于未定义的行为是不可预测的,因此在更积极的设置下,编译器在许多情况下会进行更积极的优化,其中许多可能没有太多直观意义,但是,嘿,这是未定义的行为,所以无论如何你都不应该抱有期望。

请注意,尽管在很多情况下编译器可以确定函数未正确返回,但在一般情况下这是 halting problem。 .在运行时自动执行此操作会带来成本,这违反了不为不使用的东西付费的理念。虽然 gccclang 都实现了 sanitizers检查未定义行为之类的事情,例如使用 -fsanitize=undefined 标志将在运行时检查未定义行为。

关于c++ - 在 C++ 和优化中缺少返回值的不稳定行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26237432/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com