这么有限的寄存器,怎么会有这么多的寄存器变量呢?

How can there be so many register variables, with such a limited number of registers?

我在 C 上胡思乱想,然后我意识到,如果我声明了一堆寄存器变量,这些值不会被覆盖吗?从我从汇编中可以看出,微处理器中没有大量的寄存器,不足以满足我创建的需求。 C如何保留所有值?

不要求所有用 register 声明的变量都必须保存在 CPU 寄存器中。

C 标准是这样说的:

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.

参考:ISO C11 N1570 draft,6.7.1 第 6 段。请注意,它甚至没有提到 CPU 寄存器。

符合规范的编译器可以简单地忽略所有 register 关键字(除了对获取 register 对象的地址施加一些限制之外)。

实际上,大多数编译器会尽可能多地在 CPU 寄存器中放置尽可能多的 register 变量。

事实上,现代优化编译器可能比大多数程序员更擅长寄存器分配——特别是因为他们可以在您每次修改程序后重新编译程序时重新计算寄存器映射。

这些天的常识是 register 关键字没有提供太多好处。

旧的编译器会尽可能多地为 register 变量分配寄存器(在某些情况下,这个数字是 0)并在堆栈上分配剩余的变量。

现代编译器通常会忽略 register 关键字。他们使用复杂的寄存器分配器,自动将尽可能多的变量保存在寄存器中。

您可以依赖的 register 的唯一效果是,如果您尝试获取寄存器变量的地址,您会收到一条诊断消息。否则,寄存器变量的行为就像自动变量一样。

变量通常存储在堆栈中。也就是一块内存。变量的值通常加载到寄存器中进行操作,如果要操作另一个变量,则将其移回堆栈(保存)。通常变量甚至没有加载到寄存器中,而是在堆栈上进行操作。

register 暗示编译器 可以 将变量保存在寄存器中。您不能强制编译器使用比目标体系结构上存在的更多的寄存器,原因很明显,这是不可能的。


在 C 语言中,register 关键字仅表示不能获取变量的地址。这会阻止您做任何会阻止编译器将其保存在寄存器中的事情,但并不 要求 将其保存在寄存器中。

来自https://en.cppreference.com/w/c/language/storage_duration

The register specifier is only allowed for objects declared at block scope, including function parameter lists. It indicates automatic storage duration and no linkage (which is the default for these kinds of declarations), but additionally hints the optimizer to store the value of this variable in a CPU register if possible. Regardless of whether this optimization takes place or not, variables declared register cannot be used as arguments to the address-of operator, cannot use alignas (since C11), and register arrays are not convertible to pointers.

多年来它并没有真正做任何事情:优化编译器已经尽可能将 vars 保留在 regs 中。对于全局变量或已占用其地址的变量,则可能仅针对函数的一部分,如果无法优化变量,则将结果存储回内存。


顺便说一句,register 在 C++ 中被正式弃用,C++17 实际上将其从语言中移除。 https://en.cppreference.com/w/cpp/language/storage_duration.


相关:GNU C 有 register int foo asm("eax");(或任何其他寄存器),但即便如此,也只有 保证 在用作内联操作数时有效-asm 语句用于局部变量。在当前的 GCC 版本中,它确实会导致编译器将该寄存器用于变量,除非它需要将其溢出/重新加载到跨函数调用或其他任何方式的堆栈内存中。

https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html

但是在 GNU C 中,您可以使用 global 寄存器变量,其中一个寄存器在程序的整个生命周期内专用于全局,这会损害代码的优化t 使用那个变量。这是一个有趣的选项,但不是您应该使用的选项。

C 旨在允许编译器在解析函数时为函数生成汇编代码,而不是必须读取整个函数、检查它,然后再生成代码。已将程序解析为:

的编译器
int test(void)
{
  int x=0,y=0;
  int *p = &y;

  while(x < 10)
  {
    x++;
    foo();
    x++;
    *p *= 3;
    x++;
    bar();
    ...

将无法知道 x 的值是否可以在调用 foo and/or 对 *p 或操作的过程中安全地保存在寄存器中foo 是否有可能改变 x.

的值

register 关键字的目的是有效地告诉编译器 安全 在函数调用或操作之间将对象的值保存在寄存器中写入指针,即使它没有看到代码可能对对象所做的一切。如果将对象的地址传递给嵌套函数不违反约束,那么这种含义即使在今天也可能有用,但是允许编译器假设在使用命名对象左值的任何上下文中,所有操作都将涉及命名对象-对象左值。如果一个对象的地址从未被采用,则不需要限定符来邀请这样的假设,但是在对象的地址 被采用但在涉及该对象的冲突操作中没有持久存在的情况下,例如限定符可以为编译器提供它本来没有的信息。