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

use*_*797 2 c++ return vector undefined-behavior

假设你用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;
}
Run Code Online (Sandbox Code Playgroud)

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

$ 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
Run Code Online (Sandbox Code Playgroud)

现在假设您正在尝试调试函数max_length.在生产模式下,你得到错误的答案,所以你在调试模式下重新编译,现在当你运行它一切正常.

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

  1. 为什么编译器甚至同意在没有return语句的情况下编译函数?遗留代码是否需要此功能?

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

Sha*_*our 9

这是退出值返回函数末尾的未定义行为,这在草案C++标准部分中有所描述.6.6.31 返回语句说:

流出函数末尾相当于没有值的返回; 这会导致值返回函数中的未定义行为.

编译器不需要发出诊断,我们可以从1.4 实现合规性部分看到这一点:

可诊断规则集包含本国际标准中的所有语法规则和语义规则,但那些包含"无需诊断"的明确表示法或被描述为导致"未定义行为"的规则除外.

虽然编译器通常会尝试捕获各种未定义的行为并产生警告,但通常您需要使用正确的标志集.为了gccclang我发现以下一组标志是有用的:

-Wall -Wextra -Wconversion -pedantic

一般情况下,我鼓励您使用警告将警告变为错误-Werror.

编译器因在优化阶段利用未定义的行为而臭名昭着,请参阅通过查找死代码查找未定义的行为错误以获取一些好的示例,包括臭名昭着的Linux内核空指针检查删除处理此代码的位置:

struct foo *s = ...;
int x = s->f;
if (!s) return ERROR;
Run Code Online (Sandbox Code Playgroud)

gcc推断,因为s在被deferenced s->f;,自提领一空指针是未定义的行为则s不能为空,因此优化掉if (!s)在下一行检查(从我抄答案在这里).

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

注意,虽然在很多情况下编译器可以确定函数在一般情况下没有正确返回,但这是暂停问题.在运行时自动执行此操作会产生违反不使用哲学的成本的成本.虽然两者gccclang落实消毒杀菌剂使用检查之类的不确定的行为,例如-fsanitize=undefined标志将检查在运行时不确定的行为.


归档时间:

查看次数:

177 次

最近记录:

11 年,2 月 前