当前位置:  开发笔记 > 编程语言 > 正文

为什么我下面的第二个片段显示未定义的行为?

如何解决《为什么我下面的第二个片段显示未定义的行为?》经验,为你挑选了1个好方法。

二者clangg++似乎是符合在C++标准的段落[expr.const]/5的最后一个版本.以下代码段为两个编译器打印11.查看实例:

#include 
void f(void) {
  static int n = 11;
  static int* temp = &n;
  static constexpr int *&&r = std::move(temp);

  std::cout << *r << '\n';
}

int main()
{
  f();
}

根据我对本段的理解,两个编译器都应该打印2016下面的代码.但他们没有.因此,我必须得出结论,代码显示未定义的行为,因为clang打印任意数字并g++打印0.我想知道为什么它是UB,考虑到例如标准的N4527草案?实例.

#include 
void f(void) {
  static int n = 11;
  static int m = 2016;
  static int* temp = &n + 1;
  static constexpr int *&&r = std::move(temp);

  std::cout << *r << '\n';
}

int main()
{
  f();
}

编辑

我习惯不满足于只是说代码是UB的答案,或者显示未定义的行为.我总是喜欢进行更深入的调查,有时候,就像现在一样,我很幸运能够更多地了解编译器是如何构建的.这就是我在这种情况下发现的:

双方clangGCC似乎以消除任何未使用的变量,比如m,从代码,对任何优化级别大于-O0.GCC似乎用静态存储持续时间对局部变量进行排序,变量放在堆栈上的方式相同,即从高地址到低地址.

因此,clang如果我们更改优化级别,-O0我们2016将按预期打印数字.

GCC,除此之外,我们还改变了定义

static int* temp = &n + 1;

static int* temp = &n - 1;

我们还将获得2016代码打印的数字.



1> Kerrek SB..:

我认为这里没有任何微妙之处.&n + 1你可以考虑位置的一个数组的一个接一个点n,因此它不构成可解除引用的指针,尽管它是一个完全有效的指针.因此,temp并且r是完美的constexpr变量.

你可以r像这样使用:

for (int * p = &n; p != r; ++p) { /* ... */ }

这个循环甚至可以出现在constexpr函数中.

当您尝试取消引用时r,行为当然是未定义的,但这与常量表达式无关.


@Belloc:因为你的"常识"是一个幻想的假设.你是怎么想出来的?你是否真的在问为什么UB没有产生你预期的结果?来吧,你比这更好!:)
@Belloc:不,这不合理.C++语言中甚至没有任何内容可以证明这种可能性.它可能是迷信或信仰,但不是理性.
@Belloc:但空中报价和幻影的应用并不构成理由.您需要遵循语言规则,并且这些规则中没有任何内容表明指针的任何此类属性.
推荐阅读
谢谢巷议
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有