首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >clang++ 9.0如何神奇地治愈吊灯中悬空参考的使用?

clang++ 9.0如何神奇地治愈吊灯中悬空参考的使用?
EN

Stack Overflow用户
提问于 2019-12-18 19:35:20
回答 1查看 205关注 0票数 0

我在wandbox上做了实验,希望能找到编译器警告,这有助于在lambdas中不经意地悬挂引用。我有一个这个例子,它在多个方面表现不佳:

代码语言:javascript
复制
std::array<std::function<const int *(void)>,N> createFunctions()
{
    std::array<std::function<const int *(void)>,N> fns;
    for ( int i = 0 ; i < N ; i++ ) {
        std::cout << &i << " ";
        fns[i] = [&]() {
            return &i;
        };
    }
    std::cout << "\n";
    return fns;
}

这将一组lambda填充到一个函数数组中。每个lambda返回一个指向捕获变量i的引用的指针。应该是到处都是麻烦。

我的驱动程序例程打印出指针,并取消它。

代码语言:javascript
复制
int main()
{
  auto fns = createFunctions();
  for (int j = 0 ; j < N ; j++ ) {
    if (j != 0)
      std::cout << ", ";
    std::cout << fns[j]() << ": " << *fns[j]();
  }
  std::cout << "\n";
  return 0;
}

如果将此lambda修改为通过复制传递i,则会得到如下输出--4个指针和4个具有唯一值的指针:

代码语言:javascript
复制
0x7ffc80e65358 0x7ffc80e65358 0x7ffc80e65358 0x7ffc80e65358 
0x7ffc80e65380: 0, 0x7ffc80e653a0: 1, 0x7ffc80e653c0: 2, 0x7ffc80e653e0: 3

当它被错误地写出来时,取一个无声地超出范围的引用,它奇迹般地运行,没有出错,但是清楚地显示了它的错误。

代码语言:javascript
复制
0x7ffeebdfe9f4 0x7ffeebdfe9f4 0x7ffeebdfe9f4 0x7ffeebdfe9f4 
0x7ffeebdfe9f4: 32766, 0x7ffeebdfe9f4: 32766, 0x7ffeebdfe9f4: 32766, 0x7ffeebdfe9f4: 32766

所有四个指针都是相同的,它们所指向的值是假的。

对于所有版本的g++和高达8.x的所有版本的clang++都是如此。但是clang9.0奇迹般地以一种神奇的方式处理它:

代码语言:javascript
复制
0x7ffd193bfa30 0x7ffd193bfa30 0x7ffd193bfa30 0x7ffd193bfa30 
0x7ffd193bfa30: 0, 0x7ffd193bfa30: 1, 0x7ffd193bfa30: 2, 0x7ffd193bfa30: 3

真正有趣的部分是,指向引用的指针在所有四个lambda中都具有相同的值,但是取消引用返回四个不同的值。我试着想出一个很好的解释来解释这是怎么回事,但我很困惑。

我猜想,这是一种有意的优化,编译器已经推断出我想要做的事情,并使它成为现实。由于使用悬空引用属于“未定义行为”类别,所以编译器可以自由地做它喜欢做的事情。但情况就是这样吗?

如果编译器足够聪明,能够解决这个问题,那么它似乎就足够聪明地发出警告了,但我不明白。

EN

回答 1

Stack Overflow用户

发布于 2019-12-19 14:30:44

基于评论列车,特别是@RaymondChen的评论,很明显clang没有解决这个问题。生成的代码使它看起来像是修复了它,但它只是一些非常偶然的、未定义的行为。

几乎100%的确定性,我们可以这样说:

  • lambda函数创建了一个对变量i的悬空引用,这是一个在使用lambda之前从堆栈中消失的自动。
  • 然后调用lambda将指向堆栈上或堆栈周围的随机数据,其值可以是任何值。
  • 在大多数实现中,所指向的值显然不是所期望的。
  • 在clang 9中,足够幸运的是,悬空引用指针指向循环变量j,该变量在作用域内,并且正在从0迭代到3,这使得我们看起来好像有一个悬空引用的好副本。

main()中的循环更改为迭代lambda引用,如下所示:

代码语言:javascript
复制
   for (auto fn: fns ) {

消除循环变量j,所以现在输出是:

代码语言:javascript
复制
0x7ffc0f21a100 0x7ffc0f21a100 0x7ffc0f21a100 0x7ffc0f21a100 
0x7ffc0f21a100: 4212390, 0x7ffc0f21a100: 4212390, 0x7ffc0f21a100: 4212390, 0x7ffc0f21a100: 4212390

仍然在寻找检测这种程序错误的好方法,这给我们提供了一个永恒的人类警惕性的辅助。赫伯萨特的寿命剖面将是一个真正的好办法,如果它成为现实的话。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59398973

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档