局部变量的地址是constexpr吗?

时间:2019-04-16 00:12:22

标签: c++ c++11 constexpr memory-address

p在Bjarne Stroustrup的书“ C ++编程语言(第4版)”中。 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的例子还有其他事情?

4 个答案:

答案 0 :(得分:17)

第2页的Bjarne Stroustrup的书“ C ++编程语言(第4版)”的较早版本。 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的值。编译就可以了。

答案 1 :(得分:10)

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

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

enter image description here

答案 2 :(得分: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
}

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

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

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

总而言之,由于函数中的局部变量的地址对于该函数的每次调用可能都不相同(即,每个调用的堆栈帧可能位于内存中的不同位置),因此,变量无法在编译时确定,因此不能被限定为constexpr

答案 3 :(得分:1)

只需在指出错误的其他答案中添加一个内容,C ++标准仅允许将constexpr指针指向静态存储持续时间的对象,该指针要早于静态存储持续时间,即nullptr 。具体参见[expr.const/8] #8.2

值得注意的是:

  • 字符串文字具有静态存储期限
  • 基于声明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;
}