Pointers 地址常量表达式 在阅读C++程序设计语言第四版的时候,我一直在研究地址常量表达式。它有一个描述地址常量表达式的短段落:

Pointers 地址常量表达式 在阅读C++程序设计语言第四版的时候,我一直在研究地址常量表达式。它有一个描述地址常量表达式的短段落:,pointers,c++11,constexpr,Pointers,C++11,Constexpr,静态分配对象的地址,如全局 变量,是一个常量。但是,其值由链接器指定, 而不是编译器,因此编译器无法知道 这样的地址常数。这限制了常数的范围 指针和引用类型的表达式。例如: constexpr const char* p1 = "asdf"; constexpr const char* p2 = p1; //OK constexpr const char* p2 = p1+2; //error: the compiler does not know the value of p1 const

静态分配对象的地址,如全局 变量,是一个常量。但是,其值由链接器指定, 而不是编译器,因此编译器无法知道 这样的地址常数。这限制了常数的范围 指针和引用类型的表达式。例如:

constexpr const char* p1 = "asdf";
constexpr const char* p2 = p1;  //OK
constexpr const char* p2 = p1+2;  //error: the compiler does not know the value of p1
constexpr char c = p1[2]; //OK, c=='d'; the compiler knows the value pointed to by p1
我有两个问题

这一条非常简单——因为编译器不知道静态对象的地址,那么它如何在编译时计算第二条语句呢?毕竟,编译器不知道p1+2的值这一事实意味着p1首先必须是未知的,对吗?但是,启用了所有严格标志的g++4.8.1接受所有这些语句

例如:

这里,NP被声明为地址常量表达式,即 本身是常量表达式的指针。这在以下情况下是可能的: 地址是通过将地址运算符应用于 静态/全局常量表达式

如果我们将N声明为不带constexpr的简单const,这也会起作用。但是,p1必须使用constexpr显式声明,以便p2成为有效语句。否则我们会得到:

错误:“p1”的值在常量表达式中不可用


为什么呢?据我所知,asdf是常量字符[]。

N3485包含关于地址常量的表达式

地址常量表达式是转换后的prvalue核心常量表达式,根据上下文的要求。。。计算为具有静态存储持续时间的对象地址的指针类型

字符串文字的第三个字符对象就是这样一个对象,参见2.14.5中的详细说明,不少于第一个字符对象

请注意,这里不使用variable,而使用object,因此我们可以访问数组元素和类成员以获得地址常量表达式,前提是数组或类对象具有静态存储持续时间,并且访问不会违反核心常量表达式的规则

从技术上讲,链接器将在目标文件中执行重新定位:

constexpr const char *x = "hello";
extern constexpr const char *y = x + 2;
我们将把它编译成一个对象文件,看看它是做什么的

[js@HOST1 cpp]$ clang++ -std=c++11 -c clangtest.cpp
[js@HOST1 cpp]$ objdump --reloc ./clangtest.o 

./clangtest.o:     file format elf32-i386

RELOCATION RECORDS FOR [.rodata]:
OFFSET   TYPE              VALUE 
00000000 R_386_32          .L.str


[js@HOST1 cpp]$ objdump -s -j .rodata ./clangtest.o 

./clangtest.o:     file format elf32-i386

Contents of section .rodata:
 0000 02000000                             ....            
[js@HOST1 cpp]$ 
链接器将获取节中已经存在的值,并将其添加到符号的值中,通过该值表示重新定位的value属性引用的符号表中的地址。在本例中,我们添加了2,因此Clang/LLVM在节中硬编码了a2

但是,p1必须使用constexpr显式声明,以便p2成为有效语句

这是因为你依赖于它的价值,而不是它的地址来保持不变。一般来说,请参见下文,您必须事先将其标记为constexpr,以便编译器在该点上可以验证以后的任何读取访问是否完全依赖于获取常量。您可能希望按如下所示对其进行更改,并看到它正常工作,我认为,因为整数和枚举类型的初始化常量对象有一个特殊情况,您甚至可以在constexpr上下文中从下面的p1数组中读取,即使它没有标记为constexpr。然而,我的叮当声似乎拒绝了它

const char p1[] = "asdf";
constexpr const char *x = p1 + 2; // OK!
constexpr char y = p1[2]; // OK!

N3485包含关于地址常量的表达式

地址常量表达式是转换后的prvalue核心常量表达式,根据上下文的要求。。。计算为具有静态存储持续时间的对象地址的指针类型

字符串文字的第三个字符对象就是这样一个对象,参见2.14.5中的详细说明,不少于第一个字符对象

请注意,这里不使用variable,而使用object,因此我们可以访问数组元素和类成员以获得地址常量表达式,前提是数组或类对象具有静态存储持续时间,并且访问不会违反核心常量表达式的规则

从技术上讲,链接器将在目标文件中执行重新定位:

constexpr const char *x = "hello";
extern constexpr const char *y = x + 2;
我们将把它编译成一个对象文件,看看它是做什么的

[js@HOST1 cpp]$ clang++ -std=c++11 -c clangtest.cpp
[js@HOST1 cpp]$ objdump --reloc ./clangtest.o 

./clangtest.o:     file format elf32-i386

RELOCATION RECORDS FOR [.rodata]:
OFFSET   TYPE              VALUE 
00000000 R_386_32          .L.str


[js@HOST1 cpp]$ objdump -s -j .rodata ./clangtest.o 

./clangtest.o:     file format elf32-i386

Contents of section .rodata:
 0000 02000000                             ....            
[js@HOST1 cpp]$ 
链接器将获取节中已经存在的值,并将其添加到符号的值中,通过该值表示重新定位的value属性引用的符号表中的地址。在本例中,我们添加了2,因此Clang/LLVM在节中硬编码了a2

但是,p1必须使用constexpr显式声明,以便p2成为有效语句

这是因为你依赖于它的价值,而不是它的地址来保持不变。一般来说,请参见下文,您必须事先将其标记为constexpr,以便编译器在该点上可以验证以后的任何读取访问是否完全依赖于获取常量。你可能想把它改一改 我认为,由于整型和枚举类型的初始化const对象有一个特殊情况,您甚至可以在constexpr上下文中从下面的p1数组中读取,即使它没有标记为constexpr。然而,我的叮当声似乎拒绝了它

const char p1[] = "asdf";
constexpr const char *x = p1 + 2; // OK!
constexpr char y = p1[2]; // OK!

谢谢第二个答案,我知道了。在g++4.8.1中,我还必须将constexpr放在数组的声明中,以避免出现错误。这很奇怪,因为初始值设定项字符串是常量,所以数组元素。。。但是,对于对象文件,我是个新手。你能用另一句话给我解释一下这个位置吗clang开发者确认这是规范中的灰色区域,其目的可能不是规范中的字面意思。你是说第一个问题在灰色区域内?@quentin no第二个问题。第一点很清楚。我建议给比亚恩发一封邮件。总之,这里的注释是constexpr const char*p2=p1+2//书中的错误是否恰当?还有,为什么在示例中使用extern和初始化:?第二个答案是Thx,我知道了。在g++4.8.1中,我还必须将constexpr放在数组的声明中,以避免出现错误。这很奇怪,因为初始值设定项字符串是常量,所以数组元素。。。但是,对于对象文件,我是个新手。你能用另一句话给我解释一下这个位置吗clang开发者确认这是规范中的灰色区域,其目的可能不是规范中的字面意思。你是说第一个问题在灰色区域内?@quentin no第二个问题。第一点很清楚。我建议给比亚恩发一封邮件。总之,这里的注释是constexpr const char*p2=p1+2//书中的错误是否恰当?还有,为什么在示例中使用extern和初始化:?