在C中注册关键字?

register关键字在C语言中做什么? 我已经读过它用于优化,但没有在任何标准中明确定义。 它是否仍然相关?如果是,那么你什么时候使用它?

这对编译器来说是一个暗示,这个variables将被大量使用,如果可能的话,你build议把它保存在一个处理器寄存器中。

大多数现代编译器都是自动完成的,并且比我们人类更擅长挑选它们。

我很惊讶,没有人提到,即使编译器决定在内存中保存variables,而不是在寄存器中,也不能取寄存器variables的地址。

所以使用register你什么都没赢(反正编译器会自己决定把variables放在哪里)并丢失&运算符 – 没有理由使用它。

它告诉编译器尝试使用CPU寄存器而不是RAM来存储variables。 寄存器在CPU中,访问速度比RAM快得多。 但是这只是编译器的一个build议,并不一定遵循。

我知道这个问题是关于C的,但是对于C ++来说,同样的问题已经被封闭为这个问题的完全重复。 因此这个答案可能不适用于C.


最新的C ++ 11标准草案N3485在7.1.1 / 3中说:

register指定符是实现的一个提示,因此声明的variables将被大量使用。 [ 注意:提示可以忽略,在大多数情况下,如果取得variables的地址,它将被忽略。 此用法已弃用… – 结束注释 ]

在C ++中(但不在 C中),标准并没有声明你不能把声明register的variables的地址; 然而,因为存储在CPU寄存器中的variables在其整个生命周期中都没有与之关联的内存位置,所以试图获取它的地址将是无效的,编译器将忽略register关键字以允许获取地址。

至less有15年没有相关性,因为优化者可以做出比这更好的决定。 即使是相关的,在一个CPU架构上也有很多意义,像SPARC或M68000这样的寄存器比它在英特尔上的寄存器less得多,这些寄存器其中大部分是由编译器自己保留的。

实际上,寄存器告诉编译器该variables不与程序中的其他任何别名(甚至不是char)。

这可以被各种情况下的现代编译器利用,并且可以在复杂的代码中帮助编译器 – 编译器可以通过简单的代码自行计算出来。

否则,它没有任何用途,也不用于寄存器分配。 只要您的编译器足够现代,通常不会导致性能下降。

我已经读过它用于优化,但没有在任何标准中明确定义。

实际上它由C标准明确定义的。 引用N1570草案第6.7.1节第6段(其他版本有相同的措词):

具有存储类说明符register的对象的标识符声明表明对该对象的访问尽可能快。 这些build议的有效程度是由实施定义的。

一元&运算符可能不适用于用register定义的对象,并且register可能不会用于外部声明。

还有一些其他的(相当模糊的)规则是特定于register限定对象的:

  • 使用register定义数组对象具有未定义的行为。
    更正:register定义一个数组对象是合法的,但是你不能对这样的对象做任何有用的事情(索引到一个数组需要获取它的初始元素的地址)。
  • _Alignas说明符(C11中的新增)可能不适用于这样的对象。
  • 如果传递给va_startmacros的参数名称是register限定的,则行为是未定义的。

可能还有其他几个; 下载标准草案并search“注册”,如果你有兴趣。

顾名思义, register本意是要求一个对象存储在一个CPU寄存器中。 但随着优化编译器的改进,这变得不那么有用。 C标准的现代版本不涉及CPU寄存器,因为它们不再(需要)假定存在这样的事情(有架构不使用寄存器)。 普遍的看法是,将对象声明应用于register更容易使生成的代码恶化 ,因为它干扰了编译器自己的寄存器分配。 可能还有一些情况是有用的(例如,如果你确实知道variables被访问的频率,而且你的知识比现代优化编译器能find的更好)。

register的主要有形效果是它可以防止任何企图取得对象的地址。 作为优化提示,这并不是特别有用,因为它只能应用于局部variables,优化编译器可以自己看到这个对象的地址不被占用。

你正在搞编译器复杂的图着色algorithm。 这用于寄存器分配。 那么,主要是。 它充当了编译器的暗示 – 这是真的。 但由于不允许使用寄存器variables的地址(请记住,编译器,现在放在你的怜悯之下,将尝试采取不同的行为),但不能完全忽略它们。 某种方式告诉你不要使用它。

关键字被使用了很久,很久以前。 当只有那么几个寄存器可以用你的食指来计数时,

但是,正如我所说,弃用并不意味着你不能使用它。

在七十年代,在C语言的最初阶段,引入了register关键字,以便程序员给编译器一些提示,告诉它variables会被经常使用,而且应该是明智的保持它在处理器的内部寄存器中的值。

如今,优化器比程序员更有效地确定更可能保存在寄存器中的variables,优化器并不总是将程序员的提示考虑在内。

所以很多人错误地build议不要使用register关键字。

让我们看看为什么!

register关键字具有相关的副作用:不能引用(获取)寄存器typesvariables的地址。

build议他人不要使用寄存器的人会误以为这是一个额外的论据。

然而,知道你不能获得一个寄存器variables的简单事实,允许编译器(及其优化器)知道这个variables的值不能通过一个指针间接修改。

当在指令stream的某个点上时,一个寄存器variables的值被分配到一个处理器的寄存器中,并且该寄存器由于得到另一个variables的值而没有被使用,编译器知道它不需要重新加载该寄存器中variables的值。 这可以避免昂贵的无用内存访问。

做你自己的testing,你会得到显着的性能改善你最内在的循环。

c_register_side_effect_performance_boost

只是一个小样本(没有任何真实世界的目的)进行比较:当在每个variables之前删除register关键字时,这段代码在我的i7(GCC)上花费3.41秒, register代码在0.7秒内完成。

 #include <stdio.h> int main(int argc, char** argv) { register int numIterations = 20000; register int i=0; unsigned long val=0; for (i; i<numIterations+1; i++) { register int j=0; for (j;j<i;j++) { val=j+i; } } printf("%d", val); return 0; } 

寄存器会通知编译器,编码器认为这个variables将被写入/读取到足以certificate其存储在可用于可变用途的less数寄存器之一中。 从寄存器读取/写入通常更快,可能需要较小的操作码集。

现在,这并不是很有用,因为大多数编译器的优化器在确定是否应该使用寄存器用于该variables以及用多长时间比你更好。

在支持的C编译器上,它试图优化代码,以便将variables的值保存在实际的处理器寄存器中。

讲故事的时间!

C作为一种语言,是一台计算机的抽象。 它可以让你做一些事情,就电脑的function而言,就是操纵记忆,做math,打印东西等等。

但是C只是一个抽象。 最终,你从中提取的是汇编语言。 汇编是CPU读取的语言,如果使用它,则可以用CPU来完成。 CPU是做什么的? 基本上,它从内存中读取数据,并写入内存。 CPU不只是对内存中的数字进行math运算。 首先,你必须将一个数字从内存移到CPU内部称为寄存器的内存中。 一旦你完成了你需要做的任何事情,你可以把它移回到正常的系统内存。 为什么要使用系统内存? 注册人数有限。 在现代处理器中,你只能得到大约一百个字节的数据,而旧式的stream行处理器则更加有限(6502有三个8位寄存器供你免费使用)。 所以,你的平均math运算如下所示:

 load first number from memory load second number from memory add the two store answer into memory 

很多是…不是math。 这些加载和存储操作可能会占用一半的处理时间。 C是计算机的抽象,解放了程序员使用和处理寄存器的烦恼,由于计算机之间的数量和types不同,C把寄存器分配的责任仅仅放在编译器上。 有一个例外。

当你声明一个variablesregister ,你告诉编译器“哟,我打算把这个variables用得很多,并且/或者是短暂的,如果我是你,我会尽量保存在一个寄存器中。 当C标准说编译器不需要做任何事情时,那是因为C标准不知道你在编译什么计算机,它可能就像上面的6502那样,所有3个寄存器都需要运行,而且没有备用的logging来保存你的电话号码。 但是,当它说你不能接收地址,这是因为寄存器没有地址。 他们是处理器的手。 由于编译器不需要给你一个地址,而且由于它永远不可能有地址,所以编译器现在可以开放几个优化。 它可以,例如,总是保持在一个注册号码。 它不必担心它存储在计算机内存中的位置(除了需要再次取回)。 它甚至可以把它变成另一个variables,把它交给另一个处理器,给它一个变化的位置等等。

tl; dr:做很多math的短期variables。 不要一次宣布太多。

当启用全局寄存器分配优化(/ Oe编译器标志)时,Microsoft的Visual C ++编译器会忽略register关键字。

请参阅MSDN上的注册关键字 。

寄存器关键字告诉编译器将特定variables存储在CPU寄存器中,以便可以快速访问。 从程序员的angular度来看,register关键字用于程序中大量使用的variables,这样编译器可以加速代码。 虽然它取决于编译器是否将variables保存在CPU寄存器或主存储器中。

寄存器指示编译器通过将该特定variables存储在寄存器中然后存储在内存中来优化该代码。 这是编译器的一个要求,编译器可能会或可能不会考虑这个请求。 如果您的某些variables被频繁访问,您可以使用此function。 例如:一个循环。

还有一件事是,如果你声明一个variables作为注册,那么你不能得到它的地址,因为它没有存储在内存中。 它在CPU寄存器中获得它的分配。