C:寄存器地址还是内存地址?

C: Address on register or memory?

我对C中的关键字“register”有点困惑。 它似乎告诉编译器它应该存储在一个寄存器中,这意味着我的一些变量存储在寄存器中,一些存储在内存中?如果是这样,有没有办法找出我的值是存储在寄存器中还是内存中?

例如:

int *x =  (int*) 0x1234;

X现在好像不是指向一个寄存器,因为这样的地址是给内存用的。 但我曾多次尝试找到一个不同的地址(也使用“register”关键字)。即使在互联网上似乎也没有人关心。

所以我的主要问题是:当指针指向寄存器时,指针中的地址看起来如何?

编辑:我在另一个问题的问题末尾找不到我的主要问题的答案。我的问题不是关于“注册”这个关键词,我只是提到了它。

在现代编译器中,您在 C 中声明的对象不一定只有一个位置。为了优化您的程序,编译器可能有时将其保存在寄存器中,有时保存在堆栈中,有时保存在特定的内存位置。通常,您不需要知道是哪个。

当您获取对象的地址并使用指针时,编译器会使您的程序像对象确实具有固定地址一样工作。这通常是通过将对象放在指定的内存位置来完成的,至少在您使用它的地址期间是这样,但允许编译器通过其他方式实现程序的最终结果。

如果您使用 register 声明一个对象,根据 C 标准,您不应该获取它的地址。但是,某些编译器可能允许这样做。

在大多数体系结构中,您不能指向 CPU 寄存器,因为它们不是内存映射的。 (8051 是我能想到的一种架构,其中核心寄存器 内存映射)。

如果您指的是外围寄存器;这些 内存映射并且在地址 space 中有地址,并且声明将与您拥有的一样 - 除了您确实需要 volatile 类型修饰符。

volatile int* x =  (volatile int*)0x1234 ;

register关键字与内存映射无关外设寄存器

How does the address in a pointer look when it points on a register?

指针是内存的概念,寄存器没有地址。每个处理器都有有限的固定数量的寄存器(可能是 8 - 16 个)。

正如其他人所提到的,register 不再真正有用,甚至有时会被编译器忽略。

要了解寄存器的真正含义,请考虑以下示例:

int a = k / 53;  // k is an int defined somewhere else...
int b = a * 9;
// a is not used after the above line

现在,我们还没有用 register 声明 a,但是任何合理的编译器仍然会在寄存器中保留 a。

这样做的原因是要执行任何操作,操作数必须在特定的寄存器中。操作的结果将例如存储在第一个操作数的寄存器中。

为了从上面的例子中计算出a,编译器会编写代码将k(大概在内存中)加载到某个寄存器(我们称它为寄存器A)并且53成另一个。计算完成后,寄存器 A 将包含操作的结果。由于我们无论如何都要在下一行将结果乘以 9,所以我们可以保持原样,将 9 加载到另一个寄存器并相乘。将值存储到内存中,然后将其加载回寄存器只会浪费很多时间。

请注意,用 volatile 声明 a 会阻止这样的优化并强制编译器实际存储和加载 a。 (虽然 volatile 对这里的这个例子根本没有任何意义,并且如果你不这样做,例如,连接特殊类型的硬件,就几乎没有用。)

存储 class 说明符 register 是在 C 语言的第一个版本中创建的,用于指示编译器使用其中一个处理器寄存器来保存具有允许更快访问范围的变量。当时代码编写需要程序员的大量关注,因为编译器并不聪明,无法检测或纠正错误,从而导致错误代码。

无论如何,在 C 标准的发展过程中,有一些技巧使一些细节难以理解。

存储 class 说明符 register 就是其中之一。因此,在评论它们之前,让我们开始查看规格。

来自ISO/IEC9899:2011,C11标准:

§6.7.1 Storage-class specifiers

A declaration of an identifier for an object with storage-class specifier register suggests that access to the object be as fast as possible. The extent to which such suggestions are effective is implementation-defined. (See note 119)

还有注释 119:

NOTE

The implementation may treat any register declaration simply as an auto declaration. However, whether or not addressable storage is actually used, the address of any part of an object declared with storage-class specifier register cannot be computed, either explicitly (by use of the unary & operator as discussed in 6.5.3.2) or implicitly (by converting an array name to a pointer as discussed in 6.3.2.1). Thus, the only operator that can be applied to an array declared with storage-class specifier register is sizeof.

这意味着存储说明符 register 建议 编译器使用处理器寄存器或任何其他方法来尽可能快地访问变量,但编译器可以做出不同的决定。它可以将声明的变量视为标准 auto 变量。

在最后一种情况下,在技术上可以获得存储地址,但如注释中所述,这是标准明确禁止的,但也在 constraints 中强制执行§6.5.3.2 地址和间接运算符:

The operand of the unary & operator shall be either a function designator, the result of a [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier.

当然,一些不兼容的编译器可能会让您应用运算符。

现在回到你的问题:

  1. int *x = (int*) 0x1234; X does not seem to point on a register now, because such addresses are for memory. But I have tried several times to find a different looking address (also using "register" key word). Even on the internet nobody seems to care.

您的示例是错误的,您声明了一个指向 int 的指针并为其分配了任意地址。这与 register 存储说明符无关。

声明 register int *x = (int*) 0x1234;,然后尝试将 & 运算符应用于 x,如 int **pp = &x; 中那样(请注意,我们正在声明一个指向 int 这就是我们获取指向 int).

的指针地址的结果

您将在兼容的编译器上遇到错误。

  1. So my main quesition: How does the address in a pointer look when it points on a register?

答案很简单:它不像任何东西,因为它不存在于标准 C