地址常量表达式 [英] Address Constant Expressions

查看:208
本文介绍了地址常量表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


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

  constexpr const char * p1 =asdf; 
constexpr const char * p2 = p1; // OK
constexpr const char * p2 = p1 + 2; //错误:编译器不知道p1的值
constexpr char c = p1 [2]; // OK,c =='d';编译器知道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被声明为地址常量表达式,即
指针本身是一个常量表达式。 (当
的地址是通过将地址运算符应用到
static /全局常量表达式生成的时候,这是可能的)



$ b b

如果我们将 N 简单地声明为 const 而不使用 constexpr 。但是, p1 必须使用 constexpr 明确声明,以便 p2 是一个有效的语句。否则,我们得到:


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


为什么? asdf就是我知道的 const char []


< div地址常量表达式


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


字符串文字的第三个字符对象是这样一个对象(参见2.14.5的详细说明),而不是第一个字符。



请注意,这里没有使用变量,而是对象(因此我们允许访问数组元素以及类成员来获取地址常数表达式,只要数组或类对象具有静态存储持续时间,并且访问不违反核心常量表达式的规则)。



技术上有一个重定位链接器将执行的目标文件:

  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:文件格式elf32-i386

[.rodata]的迁移记录:
OFFSET TYPE VALUE
00000000 R_386_32 .L.str


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

./ clangtest.o:文件格式elf32-i386

.rodata节的内容:
0000 02000000 ....
[js @ HOST1 cpp] $

链接器将获取段中已有的值,并将其添加到符号的值它在重定位的VALUE属性引用的符号表中的地址(在我们的例子中我们添加 2 ,所以Clang / LLVM硬编码 2 在部分)。


但是,p1必须使用constexpr显式声明为了p2一个有效的语句。


这是因为你依赖它的值,而不是它的地址,一般来说(见下文),你必须事先将它标记为constexpr,以便编译器在这一点可以验证任何后来的读访问肯定依赖于获得一个常量。你可能想要改变它,如下所示,并看到它的工作(我THINK,因为有一个特殊情况下初始化的const对象的积分和枚举类型,你甚至可以从下面读 p1 在constexpr上下文中,即使没有标记 constexpr ,但我的clang似乎拒绝了)

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


I'm delving into address constant expressions while reading "C++ Programming Language 4th edition" book. It has a short paragraph which describes address constant expressions:

The address of a statically allocated object, such as a global variable, is a constant. However, its value is assigned by the linker, rather than the compiler, so the compiler cannot know the value of such an address constant. That limits the range of constant expressions of pointer and reference type. For example:

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

I have two questions.

  1. This one is rather trivial - since the compiler doesn't know that address of a static object, then how can it evaluate the second statement during compile time? After all, the fact that the compiler doesn't know the value of p1+2, implies that p1 has to be unknown in the first place, right? g++ 4.8.1 with all the rigorous flags turned on accepts all these statements, though.

  2. As exemplified in this topic:

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

Here, NP is declared as an address constant-expression, i.e. an pointer that is itself a constant expression. (This is possible when the address is generated by applying the address operator to a static/global constant expression.)

This would also work if we declared N as simply const without constexpr. However, p1 has to be declared explicitly using constexpr in order to p2 be a valid statement. Otherwise we get:

error: the value of ‘p1’ is not usable in a constant expression

Why is that? "asdf" is of const char[] as far as I know.

解决方案

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!

这篇关于地址常量表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆