c++ 为什么在constexpr中,超出作用域的变量的地址等于零?

7y4bm7vi  于 2023-08-09  发布在  其他
关注(0)|答案(3)|浏览(111)

关于constexpr的语义,有一些我不明白的地方。当然,在堆栈上分配的x的地址不能在编译时计算。

constexpr auto foo()
{
    int x = 5;    
    return &x;
}

static_assert(foo() == 0);

字符串
尽管如此,这段代码编译(GCC 13.2 -std=c++20),x的地址等于0,这可以在通过的static_assert声明中看到。
请参阅https://godbolt.org/z/Pjrx3cb68

plupiseo

plupiseo1#

指向对象的指针可以在常量表达式中以有限的方式使用,即使这些对象从未存在于常量表达式中,或者已经死亡:

// In your example, x does exist at compile time, but its lifetime ends after foo() exits.
// You cannot dereference a pointer &x after x is dead, but you can do some other things:
int x = 5;

// The address of a local variable is never null.
// GCC and clang compile this.
// You can even perform this comparison outside foo(), like in your assertion, when
// x has died.
static_assert(&x != nullptr);

// The distance between two addresses can be computed, if those addresses are
// constant expresssions. GCC and clang compile this.
static_assert(&x - &x == 0);

字符串
在表达式foo() == 0中,0实际上是一个空指针常量,所以你所做的等效于&x == nullptr,尽管是间接的。这个比较的结果具有实现定义的效果,因为x是死的,而foo()产生一个 * 无效的指针值 *:
当达到存储区域的持续时间的结束时,表示该存储区域的任何部分的地址的所有指针的值变为无效指针值。通过无效指针值的间接访问和将无效指针值传递给释放函数具有未定义的行为。任何其他无效指针值的使用都有实现定义的行为。

  • [basic.stdc.general] p4
    Assert:
static_assert(foo() == 0);


clang失败,GCC 14之后也失败。但是,GCC 13及更低版本将此无效指针值视为等于null,因此比较成功。请参阅live example at compiler explorer

4c8rllxm

4c8rllxm2#

在常量表达式的求值中,如果变量的生存期仅限于常量表达式的求值,则可以像在任何其他表达式求值中一样正常使用变量。(即编译器将在编译时模拟一个堆栈,用于计算常量表达式。
static_cast需要它的 * 整个操作数 *,即foo() == 0是一个 * 常量表达式 *。
在计算foo() == 0时,您正在比较指针值,但指针值不是常量表达式结果的一部分,因此编译时指针转义到运行时上下文(常量表达式中不允许)没有问题。所以没有问题。
但是,一旦foo()返回,返回的指针值就会变成 * 无效指针值 *,因为变量x的存储时间结束。除了少数例外,任何无效指针值的使用都是实现定义的。与0相比福尔斯这种实现定义的范畴。
因此static_assert(foo() == 0, "???");是否编译是由实现定义的。最初,我预计编译器通常会将foo() == 0视为false,因为如果假设成为无效指针值的指针保留其地址,则它不可能等于空指针值。然而,现在我似乎不清楚一些编译器供应商实际上期望这里的行为是什么。据我所知,他们缺乏这种实现定义的行为的文档。

tvokkenx

tvokkenx3#

这是一个已知的GCC bug:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110619,出现在GCC 13中,但在GCC Backbone.js 中已经修复,其中static_assert(foo() != 0)为true(悬空指针不等于nullptr)。在线演示:https://gcc.godbolt.org/z/5bbP16h5o
请注意,您的程序的小修改:

constexpr auto foo() {
    int x = 5;
    auto p = &x;    
    return p;
}

static_assert(foo() != 0);

字符串
(see p引入并返回的局部变量)也让GCC 13感到满意:https://gcc.godbolt.org/z/Wx1dMzT3K

相关问题