【问题标题】:Address of register variable寄存器变量地址
【发布时间】:2010-11-18 09:07:42
【问题描述】:

在 C 中,我们不能使用 & 来找出寄存器变量的地址,但在 C++ 中我们可以这样做。为什么它在 C++ 中合法,但在 C 中不合法?有人可以深入解释这个概念。

【问题讨论】:

  • 您的问题自相矛盾。您首先声明您不能在 C 中做到这一点,然后您问“在 C 中怎么可能”。请重述。
  • @William 你是对的。抱歉,问题是“在 C 中,我们不能使用 & 运算符来找出寄存器变量的地址,但在 C++ 中我们可以做同样的事情。在 C++ 中怎么可能,但在 C 中却不行?有人可以解释一下这个概念吗? -深度。”
  • 我修复了这个问题来表达他的意思

标签: c++ c keyword register-allocation


【解决方案1】:

这是C99 standard (pdf) 的第 6.7.1 节(脚注 101)的摘录:

实现可以将任何register 声明简单地视为auto 声明。但是,无论是否实际使用可寻址存储,都无法计算使用存储类说明符寄存器声明的对象的任何部分的地址,或者显式(通过使用一元 & 运算符作为6.5.3.2 中讨论)或隐式(通过将数组名称转换为 6.3.2.1 中讨论的指针)。因此,唯一可以应用于使用存储类说明符 register 声明的数组的运算符是 sizeof

以及来自C++ standard (pdf) 的第 7.1.1 节第 3 段:

register 说明符与auto 说明符具有相同的语义,并暗示了这样声明的对象将被大量使用的实现。 [注意:提示可以被忽略,并且在大多数实现中,如果获取对象的地址,它将被忽略。 ——尾注]

关于register的趣闻

C++ 组(WG21)wants to deprecate register

register 关键字的作用很小,它提供的只是一个提示,说明通常会被忽略。在这个版本的标准中应该弃用它,释放保留的名称以供将来的标准使用,就像 auto 这次因为同样无用而被重用。

2009 年 3 月会议记录:

CWG 的共识是赞成弃用 register

看看 C99 小组 (WG14) said about register (pdf) 在一次会议上做什么:

普遍同意弃用“auto”关键字。我们是否应该要求 WG21 回到 以前使用“register”(没有地址)?不,这不会与 WG21 一起飞行。

【讨论】:

  • 是的。另一件事要添加到我的 C 和 C++ 之间的任意差异列表中。我很想听听改变这一点的理由。
  • 在过去,程序员将变量指定为寄存器变量是有意义的,因为编译器不是那么好。现在编译器的优化工作几乎总是比程序员做得更好,因此将其更改为提示符合编译器技术的进步。
  • 我并不是说“寄存器”有用,只是在 C 和 C++ 之间存在任意差异是一个坏主意。 C++ 版本的“寄存器”任意不同,但没有更多用处。它只是添加到使用 C++ 编译器编译的代码中,而不是使用 C 编译器编译的代码。我曾(天真地)期望 C 和 C++ 标准会随着时间的推移趋于收敛,或者至少不会发散。
  • @Mark Bessey:它们是两种不同的语言。我不知道你对不同的语言有什么期望,但对我来说,分歧听起来很合理。任意差异都很好——因为它们是不同的
  • 在 WG21 章程的某一点上明确规定不引入 C++ 和 C 之间的任意差异,并且 C 标准引入了 C++ 的几个特性,这些特性与现有的 C 用法不冲突,比如单行 cmets。
【解决方案2】:

register 关键字只是一个提示,可以忽略。大多数 C++ 编译器一直忽略它,但是如果您获取变量的地址或创建对它的引用,任何 C++ 编译器都会忽略它。

另一方面,C++ 编译器必须 忽略“注册”,因为您获取了变量的地址。从理论上讲,编译器可以将它存储在一个寄存器中,并为您提供一些魔术指针值,该值以某种方式映射到幕后的寄存器,但这将是很多工作,收获很少,所以没有编译器(我知道)做类似的事情。

由于寄存器在 C 中也是可忽略的,我怀疑明确禁止获取寄存器变量的地址只是为了减轻 C 编译器的检查负担。

C++标准的相关部分是7.1.1.3:

寄存器说明符与自动说明符具有相同的语义,并暗示了这样声明的对象将被大量使用的实现。 [注意:提示可以被忽略,并且在大多数实现中,如果获取对象的地址,它将被忽略。 ——尾注]

【讨论】:

  • 它还用于提醒用户如果他们使用变量的方式会阻止其存储在寄存器中。所以如果有一个紧密的循环,我可以给未来的维护者一个提示,如果他们试图在某个地方传递寄存器变量的地址,编译器就会给他们一个错误。
【解决方案3】:

很抱歉回答的太晚了。

问题在于,在 C 中,register 最初意味着将值存储在寄存器中,这就是为什么只能使用 intchar 的原因。但随着时间的推移,尤其是标准 C++,它扩展到“快速访问”而不是“在 CPU 的寄存器中”。 所以在 C++ 中,数组可能是 register 类型,但我们知道不可能将数组存储在 CPU 寄存器中。因此,在逻辑上寻址 C++ 寄存器(在上述意义上)是可以的,但如果这些值实际上在 CPU 寄存器中,则仍然没有意义。

【讨论】:

    【解决方案4】:

    我假设如果不是为了 C 兼容性,该关键字甚至不会进入该语言。虽然我不能以任何权威说话,但如果是这样,在我看来,除了标准强制执行的“编译器比你更聪明”条款之外,它似乎还有一个实际的理由合法:C++ 无需比 C 更容易获得许可。具体来说:成员函数和引用。

    因为成员函数需要一个隐式的this 参数,所以不可能从声明为register 的对象中调用它们。在 C 中,没有什么禁止你说 register struct X x;,因此在 C++ 中必须允许使用这种语言 [因为 C 兼容性是关键字甚至存在的全部原因]。但是,如果您禁止调用成员函数以及获取地址,那也包括初始构造函数调用。本质上,它不适用于非 POD 类型。因此,您最终会得到一个仅对一小部分合法类型有效的存储类说明符,而其余所有说明符都可以用于任何事情。

    您也不能创建对此类对象的引用,尽管从技术上讲,编译器不必将引用视为指针。 register int i; int& x; 不需要为两个变量留出空间,但如果你稍后再做&x,你最终会得到一个指向i 的指针。因此,必须使初始构造非法。虽然这似乎不是问题,但由于 C 中无论如何都不存在引用,回到我们之前的观点,使用 register 说明符声明的 POD 类型不能再被复制。编译器提供的复制构造函数的格式为X::X(const X&)X::X(X&)(视情况而定)。

    因此,为了保持 C 兼容性,他们必须使 register 唯一作为存储类说明符,因为它不适用于所有类型,并在其他地方修改标准的至少两个不同部分 [以指定您不能创建对使用 register 说明符声明的变量的引用,并且无法以某种方式解决 POD 复制的引用]。或者,他们可以只说“可以获取地址”,让编译器决定是否接受请求。他们无论如何都打算做的事情。

    【讨论】:

      【解决方案5】:

      寄存器变量没有地址,它被保存在(至少它应该被保存)在一个 cpu 寄存器中。由于 register 修饰符只是一个提示,如果你强制编译器生成代码来提取它的地址,修饰符将被忽略,你最终会得到一个保存在内存中的常规变量。

      要直接回答您的问题,无论哪一个可以让您获取寄存器变量的地址(您的原始帖子自相矛盾..)都可以让您忽略自己的提示,并且至少应该发出警告。 IMO 正确的实现是禁止获取寄存器变量的地址。

      【讨论】:

      • 答案是针对register 的C 或C++ 版本吗?在问题的上下文中,注意差异可能是个好主意。
      • 看起来像上面引用的 C++。这并不重要,因为这种特殊的做法绝对不会让你一无所获,你要么让编译器忽略你的提示,要么让你直接出错,这两个结果都不是你想要的,实际上是在乞求你不要这样做!
      • 这不一定是真的!请参阅ATmega48 datasheet 的第 7.4 节中的图 7-2,以获取寄存器可寻址的机器示例。
      • 有趣,我更熟悉英特尔架构(以及我使用的少数微控制器,我从不需要获取寄存器的地址来查看它是否工作)。无论如何,这是一个 5 岁的帖子,所以你的输入有点晚了!
      【解决方案6】:

      要记住的重要一点是,“注册”只是对编译器的提示(这毫无意义;我从未见过任何速度改进,大多数编译器可能只是忽略它)。 C 和 C++ 都可以忽略您的“建议”并将变量保留在内存中。当然,如果你取变量的地址,它会强制它在内存中分配一个位置。

      C 和 C++ 只是对你可以做什么有不同的规则,因为它们是不同的语言。 C++ 设计者决定允许您获取寄存器变量的地址,因为它不会伤害任何东西。 C 不允许你这样做,因为它会强制它进入内存。

      仔细想一想,C 语言的限制可能与变量必须在块的开头声明的原因相同——编译器可以在遇到变量时为变量分配内存,而不考虑以后如何使用它函数。

      【讨论】:

        【解决方案7】:

        这只是一个有根据的猜测,但我怀疑您是否可以在 C++ 中获取寄存器的地址,因为这样的想法根本不存在。在您的特定情况下,C++ 可能不使用寄存器。请注意,存储类限定符 register 只是对编译器的提示(即使不是所有现代编译器,大多数也很乐意完全忽略它)。

        【讨论】:

          【解决方案8】:

          C 和 C++ 是两种不同的语言,具有很大的共同子集。这就是为什么它们之间有些东西是不同的。

          虽然我不明白你的问题,register 是(至少在 C++ 中)暗示可能更频繁地访问变量,仅此而已。在 C 中,这意味着您不能使用 & 一元运算符获取地址,这在当时具有一定的意义。在早期的 C 语言中,预计编译器可能不会费心为变量分配内存,因此不一定需要获取地址。

          (计算机通常有寄存器,它们是 CPU 的快速访问部分,因此是访问速度最快的存储空间。如果这会带来更好的性能,变量可能存在于寄存器中,而不是内存中。)

          如今,几乎所有编译器都足够复杂,可以比程序员更好地进行自己的分配,因此使用 register 几乎总是毫无意义。

          【讨论】:

            猜你喜欢
            • 2013-05-14
            • 1970-01-01
            • 2011-09-12
            • 1970-01-01
            • 2018-10-22
            • 1970-01-01
            • 1970-01-01
            • 2020-08-15
            • 1970-01-01
            相关资源
            最近更新 更多