在C中,我们不能使用&找出寄存器变量的地址,但在C ++中我们也可以这样做。为什么它在C ++中合法而在C中不合法?有人可以深入解释这个概念。
答案 0 :(得分:31)
以下摘录自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一起飞行。
答案 1 :(得分:5)
register关键字只是一个提示,可以忽略。大多数C ++编译器始终忽略它,但是如果你获取变量的地址或者创建对它的引用,任何C ++编译器都会忽略它。
另一方面,C ++编译器不会 忽略“register”只是因为你接受了变量的地址。从理论上讲,编译器可以将它存储在一个寄存器中,并为您提供一些魔法指针值,该值以某种方式映射到幕后的寄存器,但这样做的工作量很少,因此没有编译器(我知道)做了类似的事。
由于寄存器在C中也是可忽略的,我怀疑对寄存器变量地址的明确禁止只是为了减轻C编译器的检查负担。
C ++标准的相关部分是7.1.1.3:
寄存器说明符与自动说明符具有相同的语义,并且提供了对如此声明的对象将被大量使用的实现的提示。 [注意:提示可以忽略,在大多数实现中,如果采用对象的地址,它将被忽略。 - 后注]
答案 2 :(得分:5)
对于超级迟到的回答感到抱歉。
问题是,在C中,register
最初意味着将值存储在寄存器中,这就是为什么只能使用int
和char
的原因。但随着时间的推移,特别是标准的C ++,它扩展到“快速访问”而不是“在CPU注册”。
所以在C ++中,数组可能是register
类型,但我们知道不可能将数组存储在CPU寄存器中。因此,在逻辑上可以解决C ++寄存器(在上面的意义上),但如果值实际上在CPU寄存器中仍然没有意义。
答案 3 :(得分: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
作为存储类说明符是唯一的,因为它不适用于所有类型,并且在其他地方修改标准的至少两个不同部分[to指定您不能创建对使用register
说明符声明的变量的引用,并以某种方式解决POD复制的引用]。或者,他们可以说“可以接受地址”并让编译器决定是否兑现请求。他们计划做的事情无论如何。
答案 4 :(得分:1)
寄存器变量没有地址,它在cpu寄存器中被保存(至少它应该被保存)。由于寄存器修饰符只是一个提示,如果你强制编译器生成代码来提取它的地址,修饰符将被忽略,你最终会得到一个保存在内存中的常规变量。
要直接回答你的问题,无论哪一个让你拿一个寄存器变量的地址(你原来的帖子自相矛盾......)让你忽略自己的提示,至少应该发出警告。 IMO正确的实现是禁止获取寄存器变量的地址。
答案 5 :(得分:1)
要记住的重要一点是“寄存器”只是编译器的一个暗示(这是一个毫无意义的;我从未见过任何速度改进,大多数编译器可能只是忽略它)。允许C和C ++忽略你的“建议”并将变量保存在内存中。当然,如果你取变量的地址,它将强制它在内存中分配一个点。
C和C ++对于你可以做什么有不同的规则,因为它们是不同的语言。 C ++设计者决定允许你获取寄存器变量的地址,因为它不会伤害任何东西; C不允许你这样做,因为它会强制它进入内存。
考虑更多,C的限制可能是因为变量必须在块的开头声明 - 编译器可以在遇到变量时为变量布局内存,而不考虑它在以后如何使用功能。
答案 6 :(得分:0)
C和C ++是两种不同的语言,具有大型公共子集。这就是为什么他们之间有些不同的东西。
虽然我不明白你的问题,register
(至少在C ++中)提示可以更频繁地访问变量,仅此而已。在C中,这意味着你不能使用&
一元运算符来获取地址,这在当时具有一定的意义。在C的早期,预计编译器可能不会为变量分配内存,因此不一定要有一个地址。
(计算机通常具有寄存器,它们是CPU的快速访问部分,因此是访问速度最快的存储器。变量可能存在于寄存器中,而不是存储器中,如果这会导致更好的性能。)
如今,几乎所有编译器都足够复杂,可以比程序员更好地完成自己的分配,因此使用register
几乎总是毫无意义。
答案 7 :(得分:0)
这只是一个有根据的猜测,但我怀疑你可以在C ++中取一个寄存器的地址,因为这样的想法根本不存在。在您的特定情况下,C ++可能不使用寄存器。请注意,存储类限定符register
只是对编译器的一个提示(并且大多数(如果不是所有现代编译器都乐意完全忽略它)。