指向 C++ 寄存器的指针是否合法?

wol*_*rse 30 c++ cpu-registers memory-mapping language-lawyer

假设 C++ 编译器为 CPU 寄存器未进行内存映射的体系结构编译了代码。并且假设同一个编译器为 CPU 寄存器保留了一些指针值。

例如,如果编译器无论出于何种原因(例如优化原因),为变量使用寄存器分配(不是谈论 register 关键字),并且我们打印对该变量的引用的值,编译器将返回其中一个保留的“地址值”。

该编译器会被视为符合标准吗?

从我所能收集到的(我还没有阅读整个事情 -工作草案,编程语言 C++ 标准),我怀疑该标准没有提到 RAM 内存或操作内存之类的东西,它定义了自己的内存模型相反,指针作为地址的表示(可能是错误的)。

现在由于寄存器也是一种内存形式,我可以想象将寄存器视为内存模型一部分的实现可能是合法的。

Kam*_*Cuk 38

指向 C++ 寄存器的指针是否合法?

是的。

该编译器会被视为符合标准吗?

当然。

C++ 不知道“寄存器”,不管它是什么。指针指向对象(和函数),而不是“内存位置”。该标准描述了程序的行为,而不是如何实现它。描述行为使它变得抽象——以什么方式和方式使用什么无关紧要,只有结果才是重要的。如果程序的行为与标准所说的相符,则对象的存储位置无关紧要。

我可以提到intro.memory

  1. 内存位置要么是一个非位域的标量类型的对象,要么是一个最大的相邻位域序列,所有位域都具有非零宽度。

复合

复合类型可以通过以下方式构建:

  • 指向 cv void 或给定类型的对象或函数(包括类的静态成员)的指针,

[...] 指针类型的每个值都是以下之一:

  • 指向对象或函数的指针(该指针被称为指向该对象或函数),或
  • 超过对象末尾的指针 ([expr.add]),或
  • 该类型的空指针值,或
  • 无效的指针值。

[...] 指针类型的值表示是实现定义的。[...]

要使用指针做任何有用的事情,例如应用*运算符unary.op或比较指针expr.eq,它们必须指向某个对象(边缘情况除外,例如NULL比较的情况)。对象存储在“哪里”的符号相当模糊 - 内存存储“对象”,内存本身可以在任何地方。


例如,如果编译器无论出于何种原因(例如优化原因),为变量使用寄存器分配(不是谈论 register 关键字),我们打印对该变量的引用的值,编译器将返回一个保留的“地址值”

std::ostream::operator<<电话std::num_put和转化为void*IS %p facet.num.put.virtuals。从C99 fprintf

[转化率%]p

参数应是指向 void 的指针。指针的值以实现定义的方式转换为打印字符序列。

但请注意,来自C99 fscanf

[转换指定%]p

匹配一个实现定义的序列集,它应该与 fprintf 函数的 %p 转换可能产生的序列集相同。相应的参数应该是一个指向 void 指针的指针。输入项以实现定义的方式转换为指针值。如果输入项是在同一个程序执行期间较早转换的值,则结果的指针应比较等于该值;否则 %p 转换的行为是未定义的。

对于该对象,打印的内容必须是唯一的,仅此而已。因此,编译器必须为寄存器中的地址选择一些唯一值,并在请求转换时打印它们。从/到的转换uintptr_t也将以实现定义的方式实现。但这一切都在实现中——如何实现代码行为的实现细节对 C++ 程序员来说是不可见的。

  • 好吧,如果可能的话,这取决于体系结构,而不是 C++ 标准是否允许。在某些架构上,物理上不可能获取寄存器的地址。所以......在这些架构上编译器只是不这样做。编译器仍然可以将像“*pointer”这样的代码翻译成像“if (pointer == 0x1) { use_EAX_register; ”这样的伪代码。} elseif (指针== 0x2) { use_another_register; } elseif ( etc. etc. etc. 对于每个寄存器 } else { dereference_the_actual_pointer; }`,但是生成的代码将非常慢。 (9认同)
  • @supercat:GC实现可以依赖于[`std::declare_reachable`](https://en.cppreference.com/w/cpp/memory/gc/declare_reachable)。您认为该对象应该保持活动状态的假设是不正确的。 (2认同)

Tho*_*ews 8

指向 C++ 寄存器的指针是否合法?

是和否。在 C++ 中,register关键字如果不被弃用,则是对编译器建议,而不是要求。

编译器是否实现寄存器指针取决于平台是否支持寄存器指针或寄存器是内存映射的。有些平台的某些寄存器是内存映射的。

当编译器遇到 POD 变量声明时,允许编译器使用该变量的寄存器。但是,如果平台不支持指向寄存器的指针,编译器可能会在内存中分配变量;尤其是在获取变量的地址时。

举个例子:

int a; // Can be represented using a register.  

int b;
int *p_b = &b;  // The "b" variable may no longer reside in a register
               // if the platform doesn't support pointers to registers.  
Run Code Online (Sandbox Code Playgroud)

在许多通用平台(例如 ARM 处理器)中,寄存器位于处理器的内存区域(一个特殊区域)内。从处理器出来的这些寄存器没有地址线或数据线。因此,它们不占用处理器地址空间中的任何空间。也没有返回寄存器地址的 ARM 指令。因此,对于 ARM 处理器,如果代码使用变量的地址,编译器会将变量的分配从寄存器更改为内存(处理器外部)。