如此有限的寄存器数怎么会有这么多的寄存器变量?

时间:2018-10-08 22:32:54

标签: c variables assembly microprocessors

我在鬼混C,我意识到,按权利,如果我声明了一堆寄存器变量,值会不会被覆盖?从组装中可以看出,微处理器中没有大量寄存器,不足以满足我所创建的需求。 C如何保留所有值?

5 个答案:

答案 0 :(得分:3)

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

这是C标准所说的:

  

声明具有存储类的对象的标识符   说明符寄存器建议对对象的访问与   可能。这些建议有效的程度是   实现定义的。

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

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

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

事实上,现代优化的编译器在寄存器分配方面可能比大多数程序员都要好-尤其是因为每次修改后重新编译程序时,它们都可以重新计算寄存器映射。

如今,人们普遍认为register关键字并不能带来太多好处。

答案 1 :(得分:2)

旧的编译器将为register变量分配尽可能多的寄存器(在某些情况下,该数字为0),并在堆栈上分配其余变量。

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

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

答案 2 :(得分:2)

register提示编译器可以将变量保存在寄存器中。由于显而易见的原因,您不能强迫编译器使用比目标体系结构更多的寄存器。


在C中,register关键字仅表示无法获取变量的地址。这样可以阻止您执行任何阻止编译器将其保存在寄存器中的操作,但又不要求将其保存在寄存器中。

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

  

寄存器说明符仅适用于在块范围内声明的对象,包括函数参数列表。它指示自动存储持续时间,并且没有链接(这是此类声明的默认设置),但还提示优化器在可能的情况下将此变量的值存储在CPU寄存器中。不管是否进行这种优化,声明为register的变量都不能用作操作符地址的参数,不能使用alignas(因为C11),并且寄存器数组不能转换为指针。

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


BTW,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 寄存器变量,其中寄存器在程序的整个生命周期中都专用于全局变量,这不利于不使用该变量的代码的优化。这是一个有趣的选择,但不是您应该使用的选择。

答案 3 :(得分:1)

C旨在允许编译器在解析函数时为其生成汇编代码,而不必阅读整个函数,对其进行检查,然后再生成代码。编译器已对程序进行了以下分析:

int test(void)
{
  int x=0,y=0;
  int *p = &y;

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

无法知道x的值是否可以在对foo的调用和/或对*p的操作期间安全地保存在寄存器中,或者是否可以foo可以更改x的值。

使用register关键字的目的是有效地告诉编译器,在所有调用指针的函数调用或操作中,将对象的值保存在寄存器中是安全的。即使还没有看到代码可以处理对象的所有操作。即使在今天,即使将对象的地址传递给嵌套函数都没有违反约束,这种含义也可能有用,但是允许编译器假定在使用命名对象左值的任何上下文中,所有操作都将涉及命名-对象左值。如果从不使用对象的地址,则不需要使用限定符来提出这样的假设,但是如果对象的地址 被采用但在涉及该对象的冲突操作中不存在,则可以使用这样的限定符否则将没有编译器信息。

答案 4 :(得分:-1)

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