如何在寄存器中存储c++变量

How to store a C++ variable in a register

本文关键字:存储 c++ 变量 寄存器      更新时间:2023-10-16

我想澄清一下关于寄存器变量存储的一点:是否有一种方法来确保,如果我们已经声明了一个寄存器变量在我们的代码,它将只存储在一个寄存器?

#include<iostream>
using namespace std;
int main()
{
    register int i = 10;// how can we ensure this will store in register only.
    i++;
    cout << i << endl;
    return 0;
}

你不能。这只是对编译器的一个提示,表明该变量被大量使用。以下是C99的措辞:

使用存储类说明符register声明对象的标识符表明对该对象的访问尽可能快。这些建议的有效程度是由实现定义的。

下面是c++ 11的措辞:

register说明符是对实现的一个提示,即这样声明的变量将被大量使用。[注:提示可以忽略,并且在大多数实现中,如果变量的地址被取走,提示将被忽略。]这种用法已弃用(见D.2)。-end note]

事实上,register存储类说明符在c++ 11中已被弃用(附录D.2):

不赞成使用register关键字作为存储类说明符(7.1.1)。

注意,在C语言中不能取register变量的地址,因为寄存器没有地址。这个限制在c++中被删除了,并且获取地址几乎可以保证变量不会在寄存器中结束。

许多现代编译器直接忽略c++中的register关键字(当然,除非它以无效的方式使用)。他们只是在优化方面比register关键字有用的时候要好得多。我希望针对特定目标平台的编译器能更认真地对待它。

关键字register在C和c++中有不同的含义。在c++中,它实际上是多余的,现在甚至似乎已经被弃用了。

在C中是不同的。首先,不要从字面上理解关键字的名称,它并不总是与现代CPU上的"硬件寄存器"有关。对register变量施加的限制是不能获取它们的地址,不允许&操作。这允许您标记变量以进行优化,并确保如果您试图获取其地址,编译器会向您大喊大叫。特别是一个register变量,同时也是const合格的,它永远不能别名,所以它是一个很好的优化候选者。

在C语言中使用register系统地迫使您考虑您取变量地址的每个位置。这可能不是您想在c++中做的事情,因为c++严重依赖于对对象的引用之类的东西。这可能就是为什么c++没有从C中复制register变量的这个属性的原因。

通常这是不可能的。具体来说,人们可以采取某些措施来增加这种可能性:

使用适当的优化级别。-O2

保持变量的数量较小

register int a,b,c,d,e,f,g,h,i, ... z;  // can also produce an error
// results in _spilling_ a register to stack
// as the CPU runs out of physical registers

不要获取寄存器变量的地址。

register int a;
int *b = &a;  /* this would be an error in most compilers, but
                 especially in the embedded world the compilers
                 release the restrictions */

在某些编译器中,您可以建议使用

register int a asm ("eax");  // to put a variable to a specific register

一般来说,CPP编译器(g++)会对代码进行相当多的优化。因此,当您声明一个寄存器变量时,编译器并不一定要将该值直接存储在寄存器中。(即,代码'register int x'可能不会导致编译器将该int直接存储在寄存器中。但是,如果我们可以强制编译器这样做,我们可能会成功。

例如,如果我们使用下面的代码段,那么我们可以强制编译器做我们想做的事情。编译下面这段代码可能会出错,这表明int型实际上是直接存储在寄存器中。

int main() {
    volatile register int x asm ("eax"); 
    int y = *(&x);
    return 0;
}

对于我来说,g++编译器在这种情况下抛出以下错误:

[nsidde@nsidde-lnx cpp]$ g++ register_vars.cpp 
register_vars.cpp: In function ‘int main()’:
register_vars.cpp:3: error: address of explicit register variable ‘x’ requested

这行'volatile register int x asm ("eax")'指示编译器,将整数x存储在'eax'寄存器中,这样做不做任何优化。这将确保值直接存储在寄存器中。这就是为什么访问变量的地址会抛出错误。

或者,C编译器(gcc)本身可能会出现以下代码错误。

int main() {
    register int a=10;
    int c = *(&a);
    return 0;
}

对于我来说,在这种情况下,gcc编译器会抛出以下错误:

[nsidde@nsidde-lnx cpp]$ gcc register.c 
register.c: In function ‘main’:
register.c:5: error: address of register variable ‘a’ requested

这只是对编译器的一个提示;您不能强制将变量放入寄存器中。在任何情况下,编译器编写者可能比应用程序程序员更了解目标体系结构,因此更适合编写做出寄存器分配决策的代码。换句话说,您不太可能通过使用register实现任何目标。

register关键字是编译器必须适应具有2MB RAM的机器(在18个终端之间共享,每个终端都有一个用户登录)的时代遗留下来的。或者128-256KB内存的PC/家用电脑。此时,编译器无法真正遍历一个大函数,以确定哪个寄存器用于哪个变量,从而最有效地使用寄存器。因此,如果程序员用register给出一个"提示",编译器会把它放在寄存器中(如果可能的话)。

现代编译器在2MB的内存中不能容纳好几次,但是它们在为寄存器分配变量方面要聪明得多。在给出的例子中,我发现编译器不把它放在寄存器中是非常不可能的。显然,寄存器的数量是有限的,并且给定一段足够复杂的代码,有些变量不适合放在寄存器中。但是对于这样一个简单的例子,一个现代的编译器将把i变成一个寄存器,并且它可能直到ostream& ostream::operator<<(ostream& os, int x)的某个地方才会接触内存。

确保您正在使用寄存器的唯一方法是使用内联汇编。但是,即使这样做,也不能保证编译器不会将值存储在内联汇编块的之外。当然,你的操作系统可能会决定在任何时候中断你的程序,将所有寄存器存储到内存中,以便将CPU分配给另一个进程。

因此,除非您在内核中编写禁用所有中断的汇编代码,否则绝对没有办法确保变量永远不会击中内存。

当然,这只有在你关心安全的时候才有意义。从性能的角度来看,使用-O3编译通常是足够的,编译器通常在确定在寄存器中保存哪些变量方面做得很好。无论如何,在寄存器中存储变量只是性能调优的一个小方面,更重要的方面是确保在内部循环中不会完成多余或昂贵的工作。

这里您可以在c++中使用volatile register int i = 10来确保i被存储在寄存器中。volatile关键字将不允许编译器优化变量i