【问题标题】:How to store a C++ variable in a register如何将 C++ 变量存储在寄存器中
【发布时间】:2013-04-01 06:57:46
【问题描述】:

我想澄清一下关于寄存器变量存储的一点: 有没有办法确保如果我们在代码中声明了一个寄存器变量,它只会存储在一个寄存器中?

#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;
}

【问题讨论】:

  • C 和 C++ 不保证运行程序的硬件甚至具有“寄存器”。这只是对编译器的提示。
  • @StephenCanon 不仅仅是 C 中的一个提示,声明一个变量 register 禁止将其地址放在那里。 (就将变量实际放在寄存器中而言,这只是一个提示。)
  • 对您有帮助吗:Stack Overflohow to know where is the register variable stored? 不是您的答案,但您可能会觉得很有趣
  • 更进一步的'你为什么关心评论'。我们可以说编译器将始终将变量存储在寄存器中 - 现在是什么。我们可以说所有简单的局部变量总是存储在一个寄存器中——现在是什么。除非它有一些可见/有用的副作用,否则它没有逻辑意义。就像'pink int i = 2;'请求将变量存储在粉色内存中

标签: c++ inline-assembly cpu-registers


【解决方案1】:

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

为具有存储类说明符register 的对象声明标识符建议尽可能快地访问该对象。此类建议的有效程度由实施定义。

这是 C++11 的措辞:

register 说明符暗示了这样声明的变量将被大量使用的实现。 [ 注意:提示可以被忽略,并且在大多数实现中,如果变量的地址被获取,它将被忽略。这种用法已被弃用(见 D.2)。 ——尾注]

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

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

请注意,您不能在 C 中获取 register 变量的地址,因为寄存器没有地址。这个限制在 C++ 中被删除了,并且几乎可以保证获取地址以确保变量不会最终出现在寄存器中。

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

【讨论】:

  • 看到将每个局部变量标记为register的代码总是让我感到惊讶。
  • @PeteBecker 谁需要内存?我都是注册人!
  • 您的论点仅是 C++。 C 对寄存器变量施加了限制。
  • @JensGustedt 这仍然只是 C 中的一个提示。我在专门谈论 C++ 时注意到了这一点。我更加充实了答案。
  • 啊,让你怀念那些你可以编码 register(4) 并且 var 最终会出现在寄存器 4 中的日子。(然后你会跟随几个 gen 过程调用来进行组装东西。)
【解决方案2】:

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

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

在 C 中使用 register 会系统地迫使您考虑获取变量地址的每个地方。这可能不是您在 C++ 中想要做的事情,它严重依赖于对对象和类似事物的引用。这可能是 C++ 没有从 C 复制 register 变量的这个属性的原因。

【讨论】:

  • 我认为您在第二段的第一句话中的意思是“字面上”(或“逐字”)而不是“详细”。
【解决方案3】:

一般来说是不可能的。具体来说,可以采取某些措施来增加概率:

使用适当的优化级别,例如。 -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

【讨论】:

  • 您的论点仅是 C++。 C 对register 变量施加了限制。此外,您在 gcc 中关于 asm 的示例具有误导性。对于 gcc,这不是“建议”。如果您指定一个寄存器,该寄存器应该存在并且被使用。
  • GCC docs 已更新:register ... asm() local 变量不再保证任何东西,除了为"r" 约束选择该寄存器。实际上,GCC 仍然使用指定的寄存器; clang 不在asm 语句之外。相关:Reading a register value into a C variable
【解决方案4】:

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

【讨论】:

  • 您的论点仅是 C++。 C 对寄存器变量施加了限制。
【解决方案5】:

“register”关键字是编译器必须安装在具有 2MB RAM 的机器上的残余(在 18 个终端之间共享,每个终端都有一个用户登录)。或具有 128-256KB RAM 的 PC/家用电脑。那时,编译器无法真正运行一个大函数来确定哪个寄存器用于哪个变量,以最有效地使用寄存器。因此,如果程序员使用register 给出“提示”,编译器会将其放入寄存器(如果可能)。

现代编译器无法在 2MB 的 RAM 中多次安装,但它们在将变量分配给寄存器方面要聪明得多。在给出的示例中,我发现编译器不会将它放入寄存器中是非常不可能的。显然,寄存器的数量是有限的,并且给定一段足够复杂的代码,一些变量将不适合寄存器。但是对于这样一个简单的示例,现代编译器会将i 设为寄存器,并且它可能直到ostream&amp; ostream::operator&lt;&lt;(ostream&amp; os, int x) 内部的某个位置才会触及内存。

【讨论】:

  • 哈哈,2MB 上有 18 个终端。离开我的草坪 :-) CP-67 was supporting 60 users on half that.
  • 是的,我学校共享一台运行 RSTS-E 的 2MB PDP-11,我学校有 8 个终端和一台打印机,隔壁学校有 8+1,然后主学校有两个每个房间有 16 个终端的房间(加上散布在其他几个地方的一些随机终端)。并且有一个 C 编译器,但我们大多数人都使用当时的现代语言 Pascal。
【解决方案6】:

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

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

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

【讨论】:

    【解决方案7】:

    通常 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
    

    【讨论】:

    • 这个asm("eax") 语法正是我想要的。感谢您实际回答问题
    • 他没有链接到docs 用于包含有用信息的asm。特别是:此功能唯一受支持的用途是在调用扩展 asm 时为输入和输出操作数指定寄存器。因此不能保证任何特定值在任何特定时间都会在 eax 中,除非/直到您调用扩展 asm。这直接回到了其他人所说的:它不再意味着什么了。
    【解决方案8】:

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

    【讨论】:

    • 甚至没有关闭。 Volatile 强制编译器假设对变量的任何访问都具有可见的副作用,并且由于优化而强制这些副作用不会改变。这意味着它根本无法重新排序具有可见副作用的语句。
    猜你喜欢
    • 1970-01-01
    • 2021-05-23
    • 1970-01-01
    • 2015-04-25
    • 1970-01-01
    • 2013-05-14
    • 2013-11-11
    • 1970-01-01
    • 2019-05-22
    相关资源
    最近更新 更多