寄存器变量的地址

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

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

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

从第7.1.1节, C ++标准的第3段(pdf) :

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

关于register有趣花絮

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

register关键字提供的function非常少,提供的注释通常只会被忽略。 它应该在这个版本的标准中弃用,释放保留的名称以备将来的标准使用,就像auto这次被重复使用同样没用。

2009年3月会议记录:

CWG的共识是赞成弃用register

看看C99小组(WG14 )在会议上关于register (pdf)的内容:

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

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

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

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

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

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

抱歉超级迟到的答案。

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

我假设如果不是C兼容性,关键字甚至不会成为语言。 虽然我不能与任何权威人士交谈,但如果是这样的话,在我看来,除了简单的标准强制执行的“编译器比你更聪明”这个条款之外,它有一个实际的理由:C ++接受没有事情的地址比C更容易获得许可。 具体来说:成员函数和引用。

因为成员函数需要隐式this参数,所以不可能从声明为对象的对象中调用它们。 在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复制的引用]。 或者,他们只能说“可以接受地址”并让编制者决定是否兑现请求。 他们计划做的事情无论如何。

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

要直接回答你的问题,无论哪一个让你拿一个寄存器变量的地址(你原来的post自相矛盾……)让你忽略自己的提示,至少应该发出警告。 IMO正确的实现是禁止获取寄存器变量的地址。

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

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

考虑更多,C的限制可能是因为变量必须在块的开头声明 – 编译器可以在遇到变量时为变量布局内存,而不考虑它在函数后面的使用方式。

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

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

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

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

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