局部变量的地址是constexpr吗?

问题描述 投票:23回答:4

在Bjarne Stroustrup的书“The C ++ Programming Language(4th Edition)”中。 267(第10.4.5节地址常量表达式),他使用一个代码示例,其中局部变量的地址设置为constexpr变量。我认为这看起来很奇怪,所以我尝试使用g ++版本7.3.0运行该示例,但无法获得相同的结果。这是他的代码示例逐字(虽然略有删节):

extern char glob;

void f(char loc) {
    constexpr const char* p0 = &glob; // OK: &glob's is a constant
    constexpr const char* p2 = &loc;  // OK: &loc is constant in its scope
}

当我运行这个时,我得到:

error: ‘(const char*)(& loc)’ is not a constant expression

用g ++发生的事情我不知道,还是Bjarne的例子还有什么?

c++ c++11 constexpr memory-address
4个回答
17
投票

早先印刷了Bjarne Stroustrup的书“The C ++ Programming Language(4th Edition)”。 267有OP问题中列出的错误。当前的打印和电子副本已经“更正”,但引入了后面描述的另一个错误。它现在引用以下代码:

constexpr const char* p1="asdf";

这是可以的,因为“asdf”存储在固定的内存位置。在早期的印刷中,这本书在这里出错了:

void f(char loc) {
    constexpr const char* p0 = &glob; // OK: &glob's is a constant
    constexpr const char* p2 = &loc;  // OK: &loc is constant in its scope
}

但是,loc不在固定的内存位置。它位于堆栈上,根据调用的时间而有不同的位置。

但是,目前的第4版打印还有另一个错误。这是10.5.4的逐字代码:

int main() {
    constexpr const char* p1 = "asdf";
    constexpr const char* p2 = p1;      // OK
    constexpr const char* p3 = p1+2;    // error:  the compiler does not know the value of p1
}

这是错的。编译器/链接器确实知道p1的值,并且可以在链接时确定p1+2的值。它编译得很好。


10
投票

看来我的“C ++编程语言(第4版)”的硬拷贝中提供的10.4.5节的例子是不正确的。所以我得出结论,局部变量的地址不是constexpr

该示例似乎已在某些pdf版本中更新,如下所示:

enter image description here


3
投票

这个答案试图通过分析x86-64架构的一个例子来澄清为什么局部变量的地址不能是constexpr

考虑以下玩具函数print_addr(),它显示其局部变量local_var的地址,并递归调用n次:

void print_addr(int n) {
   int local_var{};
   std::cout << n << " " << &local_var << '\n';

   if (!n)
      return; // base case

   print_addr(n-1);  // recursive case
}

print_addr(2)的调用在我的x86-64系统上产生了以下输出:

2 0x7ffd89e2cd8c
1 0x7ffd89e2cd5c
0 0x7ffd89e2cd2c

如您所见,local_var的相应地址对于每次调用print_addr()都是不同的。您还可以看到函数调用越深,局部变量local_var的地址越低。这是因为堆栈在x86-64平台上向下(即,从较高地址到较低地址)增长。

对于上面的输出,call stack在x86-64平台上看起来如下:

                |     . . .     |
Highest address ----------------- <-- call to print_addr(2) 
                | print_addr(2) |    
                -----------------
                | print_addr(1) |
                -----------------
                | print_addr(0) | <-- base case, end of recursion
Lowest address  ----------------- Top of the stack

上面的每个矩形表示每次调用stack frame时的print_addr()。每个调用的local_var位于其相应的堆栈帧中。由于每次调用local_varprint_addr()都位于其自己的(不同的)堆栈帧中,因此local_var的地址不同。

总之,由于函数中局部变量的地址对于函数的每次调用可能都不相同(即,每个调用的堆栈帧可能位于存储器中的不同位置),这样的变量的地址可以'在编译时确定,因此不能被认定为constexpr


1
投票

只是为了添加指出错误的其他答案,C ++标准只允许constexpr指向静态存储持续时间的对象,一个超过这样的结束,或者nullptr。具体参见[expr.const/8]#8.2;

值得注意的是:

  • string-literals具有静态存储持续时间:
  • 基于声明extern变量的约束,它们本身具有静态存储持续时间或线程本地存储持续时间。

因此这是有效的:

#include <string>

extern char glob;
std::string boom = "Haha";

void f(char loc) {
    constexpr const char* p1 = &glob;
    constexpr std::string* p2 = nullptr;
    constexpr std::string* p3 = &boom;
}
© www.soinside.com 2019 - 2024. All rights reserved.