局部变量的地址是constexpr吗?
Is the address of a local variable a constexpr?
在 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 的示例是否还有其他问题?
Bjarne Stroustrup 的书 "The C++ Programming Language (4th Edition)" 在第 1 页的早期印刷。 267 有 OP 问题中概述的错误。当前的印刷版和电子版已经 "corrected" 但引入了稍后描述的另一个错误。它现在指的是以下代码:
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
}
这是错误的。 compiler/linker 确实知道 p1 的值,并且可以在 link 时确定 p1+2
的值。它编译得很好。
我的 "The C++ Programming Language (4th Edition)" 硬拷贝中提供的第 10.4.5 节中的示例似乎不正确。所以我得出结论,局部变量的地址不是 constexpr
.
该示例似乎已在某些 pdf 版本中更新,如下所示:
只是为了补充指出错误的其他答案,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;
}
这个答案试图通过分析 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
可以看到,每次调用print_addr()
,local_var
对应的地址都不一样。也可以看到函数调用越深,局部变量的地址越低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
位于其对应的堆栈帧中。由于对 print_addr()
的每次调用的 local_var
位于其自己的(不同的)堆栈帧中,因此 local_var
的地址不同。
综上所述,由于函数中局部变量的地址在每次调用函数时可能都不相同(即,每次调用的堆栈帧可能位于内存中的不同位置),因此这样的变量无法在编译时确定,因此不能限定为 constexpr
.
在 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 的示例是否还有其他问题?
Bjarne Stroustrup 的书 "The C++ Programming Language (4th Edition)" 在第 1 页的早期印刷。 267 有 OP 问题中概述的错误。当前的印刷版和电子版已经 "corrected" 但引入了稍后描述的另一个错误。它现在指的是以下代码:
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
}
这是错误的。 compiler/linker 确实知道 p1 的值,并且可以在 link 时确定 p1+2
的值。它编译得很好。
我的 "The C++ Programming Language (4th Edition)" 硬拷贝中提供的第 10.4.5 节中的示例似乎不正确。所以我得出结论,局部变量的地址不是 constexpr
.
该示例似乎已在某些 pdf 版本中更新,如下所示:
只是为了补充指出错误的其他答案,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;
}
这个答案试图通过分析 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
可以看到,每次调用print_addr()
,local_var
对应的地址都不一样。也可以看到函数调用越深,局部变量的地址越低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
位于其对应的堆栈帧中。由于对 print_addr()
的每次调用的 local_var
位于其自己的(不同的)堆栈帧中,因此 local_var
的地址不同。
综上所述,由于函数中局部变量的地址在每次调用函数时可能都不相同(即,每次调用的堆栈帧可能位于内存中的不同位置),因此这样的变量无法在编译时确定,因此不能限定为 constexpr
.