3

在阅读“C++ 编程语言第 4 版”一书时,我正在研究地址常量表达式。它有一小段描述地址常量表达式:

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

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

我有两个问题。

  1. 这个相当简单——因为编译器不知道静态对象的地址,那么它如何在编译时评估第二条语句?毕竟,编译器不知道 的值这一事实p1+2意味着p1首先必须是未知的,对吧?但是,打开所有严格标志的 g++ 4.8.1 接受所有这些声明。

  2. 本主题所示:

static constexpr int N = 3;
int main()
{
  constexpr const int *NP = &N;
  return 0;
}

在这里,NP 被声明为一个地址常量表达式,即一个本身就是一个常量表达式的指针。(通过将地址运算符应用于静态/全局常量表达式来生成地址时,这是可能的。)

N如果我们简单地声明为const没有,这也将起作用constexpr。但是,p1必须显式声明 usingconstexpr才能p2成为有效的语句。否则我们得到:

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

这是为什么?"asdf"const char[]我所知。

4

1 回答 1

5

N3485 contains about "address constant expression"

An address constant expression is a prvalue core constant expression (after conversions as required by the context) of ... pointer type that evaluates to the address of an object with static storage duration ....

The third character object of a string literal is such an object (see the elaborations on 2.14.5), not any less than the first one of it.

Note that there is no use of variable, but object here (so we are allowed to access array elements aswell as class members to get an address constant expression, provided that the array or class object has static storage duration and the access does not otherwise violate the rules of core constant expressions).

Technically there is a relocation in the object file that the linker will carry out:

constexpr const char *x = "hello";
extern constexpr const char *y = x + 2;

We will compile this down to an object file and look what it does

[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]$ 

The linker will take the value what is already in the section, and add it to the value of the symbol (by which is meant its address in the symbol table) referenced by the "VALUE" property of the relocation (in our case we added 2, so Clang/LLVM hardcoded a 2 in the section).

However, p1 has to be declared explicitly using constexpr in order to p2 be a valid statement.

That is because you are relying on its value, rather than its adress, to be constant. In general (see below) you must previously mark it as constexpr so that the compiler at that point can validate that any later read access can definitely rely on getting a constant. You may want to change it as follows and see it working (I THINK since there is a special case for initialized const objects of integral and enumeration types you can even read from the below p1 array in a constexpr context, even without it being marked constexpr. However my clang seems to reject it)

const char p1[] = "asdf";
constexpr const char *x = p1 + 2; // OK!
constexpr char y = p1[2]; // OK!
于 2013-07-15T17:38:49.047 回答