15

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

4

8 回答 8

31

以下是C99 标准 (pdf)的第 6.7.1 节(脚注 101)的摘录:

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

并且来自C++ 标准 (pdf)的第 7.1.1 节第 3 段:

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

关于有趣的花絮register

C++ 组 (WG21)想要弃用register

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

2009 年 3 月会议记录:

CWG 的共识是赞成弃用register.

看看 C99 小组 (WG14 )在一次会议上register(pdf)的看法:

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

于 2009-08-10T17:53:23.470 回答
5

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

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

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

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

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

于 2009-08-10T17:56:44.647 回答
5

对不起,超级迟到的答案。

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

于 2011-04-28T05:57:45.723 回答
3

我假设如果不是为了 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 复制的引用]。或者,他们可以只说“可以获取地址”并让编译器决定是否接受请求。他们无论如何都打算做的事情。

于 2011-04-28T07:34:40.333 回答
1

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

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

于 2009-08-10T17:53:19.413 回答
1

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

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

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

于 2009-08-10T17:55:50.203 回答
0

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

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

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

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

于 2009-08-10T17:52:03.690 回答
0

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

于 2009-08-10T17:52:23.750 回答