寄存器变量的地址

15

在 C 语言中,我们无法使用 & 来获取寄存器变量的地址,但在 C++ 中可以这样做。为什么在 C++ 中合法而在 C 中不行呢?可以有人详细解释一下这个概念吗?


您的问题自相矛盾。您首先声明无法使用C语言完成,然后又问“如何在C语言中实现”。请重新表述。 - William Pursell
@William 你是对的。 抱歉,问题是:“在C语言中,我们不能使用&运算符来查找寄存器变量的地址,但在C++中我们可以这样做。为什么C++可以但C语言不行?能否有人深入解释一下这个概念。” - Naveen
我修正了这个,以表达他想要说的意思。 - Tyler McHenry
8个回答

31
以下是来自C99标准(pdf)第6.7.1节(脚注101)的摘录:
实现可以将任何register声明简单地视为auto声明。但是,无论是否实际使用可寻址存储器,都不能计算用存储类说明符register声明的对象的任何部分的地址,无论是显式地(如6.5.3.2中所讨论的使用一元&运算符)还是隐式地(通过将数组名称转换为指针,如6.3.2.1中所讨论的)。因此,可以应用于使用存储类说明符register声明的数组的唯一运算符是sizeof
并且从C ++标准(pdf)第7.1.1节第3段: register说明符具有与auto说明符相同的语义,并提示实现声明的对象将被大量使用。[注意:如果取了对象的地址,则提示可以被忽略,在大多数实现中,如果取了对象的地址,则会被忽略。-end note] register的有趣细节

C++组(WG21)希望弃用register关键字

register关键字几乎没有什么作用,只提供了一个提示,通常被忽略。它应该在这个标准的版本中被弃用,为未来标准释放保留的名称,就像auto一样在此次使用中也被重新使用,因为它同样无用。

来自2009年3月会议的笔记:

CWG的共识是支持弃用register

看看C99组(WG14)在会议上对register说了什么(pdf)

普遍同意弃用“auto”关键字。我们是否应该要求WG21回到以前的“register”使用方式(没有地址)?不,这对WG21来说行不通。


2
耶。又有一件事情可以添加到我对C和C++之间任意差异的清单上了。我很想听听更改这个的理由。 - Mark Bessey
2
在过去,程序员将变量指定为寄存器变量是有道理的,因为编译器并不那么好。如今,编译器几乎总能比程序员更好地进行优化工作,因此将其更改为提示与编译器技术的进步保持一致。 - Martin York
2
我并不是在争论"register"是否有用,而是认为在C和C++之间存在任意差异是一个坏主意。C++版本的"register"是任意不同的,但并不更有用。它只是增加了那些能够使用C++编译器编译但不能使用C编译器编译的代码数量。我曾经(天真地)期望C和C++标准随着时间推移会趋于一致,或者至少不会分歧。 - Mark Bessey
2
@Mark Bessey:它们是两种不同的语言。我不知道你对于“不同的语言”有什么期望,但分歧听起来对我来说很合理。任意差异都没问题——因为它们是“不同的”。 - Puppy
5
曾经,WG21 的章程明确规定不引入 C++ 和 C 之间的任意差异,而 C 标准已经引入了一些 C++ 的特性,如单行注释,这些特性与现有的 C 用法没有冲突。 - Mark Bessey
显示剩余2条评论

5

非常抱歉回答晚了。

问题在于,在C语言中,register 原本的意思是将值存储在寄存器中,这就是为什么只有 intchar 可以用它来声明。但随着时间的推移,特别是在标准C++中,它的含义扩大到了“快速访问”而不是“CPU寄存器中的存储”。

因此,在C++中,数组可能是一个register类型,但我们知道不可能将数组存储在CPU寄存器中。因此,逻辑上来说,可以使用C++寄存器(以上所述的意义),但如果实际值在CPU寄存器中,则仍然没有意义。


5
register关键字仅仅是一个提示,可以忽略。大多数C++编译器总是会忽略它,但是如果您获取变量的地址或创建对其的引用,则任何C++编译器都将忽略它。
另一方面,C++编译器并不一定因为您获取了变量的地址就必须忽略"register"。理论上,编译器可以将其存储在寄存器中,并给您一些神奇的指针值,该值在幕后以某种方式映射到寄存器,但这需要付出很多工作却没有多少收益,所以(据我所知)没有编译器会这样做。
由于在C中register也是可忽略的,因此我怀疑明确禁止获取register变量地址的原因只是为了减轻C编译器的负担。
相关的C++标准部分是7.1.1.3:

register说明符与auto说明符具有相同的语义,并提示实现,声明的对象将被频繁使用。[注意:如果获取对象的地址,则提示可能被忽略,在大多数实现中,如果获取对象的地址,则它将被忽略。-注释]


它还有一个作用,就是在用户使用变量的方式会阻止其存储在寄存器中时发出警报。因此,如果存在紧密循环,我可以通过让编译器在尝试将寄存器变量的地址传递到某个地方时给未来的维护者提供错误提示来向他们提供提示。 - user295691

3
我认为,如果不考虑与C的兼容性,该关键字甚至不会出现在语言中。虽然我无法权威地表达,但如果是这样,似乎有一个实际原因使它合法,而不仅仅是一种标准强制执行的“编译器比你更聪明”的条款:C++更容易接受事物的地址而不需要许可,比C更具灵活性。具体来说,成员函数和引用。

由于成员函数需要隐式的 this 参数,所以无法从声明为 register 的对象调用它们。在C中,没有任何限制你可以说 register struct X x;, 因此在C++中必须允许这种语言。但是如果禁止调用成员函数以及获取地址,那么也就涵盖了初始构造函数调用。本质上,它不适用于非POD类型。因此,您最终得到了一种存储类说明符,它仅适用于一小部分合法类型,而其他所有类型都可以用于任何类型。

您还不能创建指向这些对象的引用,即使从技术上讲,编译器不必将引用视为指针。不需要为两个变量腾出空间,但如果稍后执行 &x,则会得到指向 i 的指针。因此,初始构造必须被禁止。虽然这似乎不是问题,因为引用在C中不存在,但回到我们之前的观点,使用 register 说明符声明的POD类型不再能够被复制。编译器提供的复制构造函数为 X::X(const X&)X::X(X&)

因此,为了保持与C的兼容性,他们必须使 register 成为一个唯一的存储类说明符,因为它不适用于所有类型,并修改标准的至少两个不同部分(以指定不能创建带有 register 说明符的变量的引用,并解决POD复制的引用)问题)。或者,他们可以只是说“获取地址没关系”,并让编译器决定是否遵守请求。他们本来就打算这样做。


1

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

直接回答你的问题,任何允许你获取寄存器变量地址的方式(你原始帖子中有自相矛盾的地方...)都会忽略你的提示,并且至少会发出一个警告。我个人认为正确的实现应该是不允许获取寄存器变量的地址。


答案是针对C还是C++版本的register?在问题的背景下,最好注意它们之间的区别。 - CB Bailey
从上面的引用看起来是C++。不过这个实践对你来说毫无意义,因为你要么让编译器忽略你的提示,要么直接出错,两种结果都不是你想要的,实际上在暗示你不要这样做! - Blindy
这并不一定是真的!请参见ATmega48数据手册第7.4节中图7-2的示例,其中寄存器是可寻址的。 - LThode
有趣的是,我更熟悉英特尔架构(以及我使用过的少数微控制器,我从来不需要获取寄存器地址来查看它是否有效)。无论如何,这是一个五年前的帖子,所以你的意见有点晚了! - Blindy

1
重要的是要记住,“register”只是编译器的提示(一个毫无意义的提示;我从未见过任何速度提升,大多数编译器可能会忽略它)。C和C++都可以忽略你的“建议”并将变量保留在内存中。当然,如果您获取变量的地址,它将强制分配内存中的一个位置。
C和C++之所以可以做不同的事情,是因为它们是不同的语言。C++的设计者决定允许您获取寄存器变量的地址,因为这不会造成任何损害;C不允许您这样做,因为它会强制将其放入内存中。
更深入地思考一下,C的限制可能是出于与变量必须在块的开头声明的相同原因——编译器可以在遇到它们时布局变量的内存,而不考虑它在函数中稍后如何使用。

0

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

虽然我不理解你的问题,但register(至少在C++中)是一个提示,表明变量可能更频繁地被访问,仅此而已。在C中,它意味着您不能使用&一元运算符获取地址,这在当时是有一定道理的。在C的早期,编译器可能不会为变量分配内存,因此可能没有地址可用。

(计算机通常具有寄存器,这是CPU的快速访问部分,因此是最快的存储器。如果将变量放在寄存器中而不是内存中,则可能会获得更好的性能。)

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


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接