关于C和C++的register关键字
近日看到一些C++代码在for循环里加入了register关键字来试图声明寄存器变量,并声称可以优化速度,那么这是不是有效的呢?我们将看到,这个写法在C语言里也许(但未必)是有效的,但是在C++语言里可能不是非常有效了。
In C++
博主在写这篇博客的时候最新C++标准是ISO/IEC 14882:2017,C++20目前只出了一个working draft,还没有出台出现正式的文件,因此我们以C++17为最新标准。
我们翻开C++17标准,里面有类似的叙述:
第C.1.6款:
Change: In C++,
registeris not a storage class specifier.
Rationale: The storage class specifier had no effect in C++.
第C.4.3款:
Change: Removal of
registerstorage-class-specifier.
Rationale: Enable repurposing of deprecated keyword in future revisions of this International Standard.
Effect on original feature: A valid C++ 2014 declaration utilizing the register storage-class-specifier is ill-formed in this International Standard . The specifier can simply be removed to retain the original meaning.
C++17标准很明确地指出,register关键字在C++17已经是一个没有任何作用(“no effect”)的关键字了,带有register存储类型标识符的程序已经是"ill formed"的了。因此我们不应该再写register关键字来声明寄存器变量。
实际上,register关键字在稍老的C++标准中也不总是有效。我们翻开最古老的C++98标准,其中的第7.1.1款第4条是这样描述register关键字的:
A
registerspecifier has the same semantics anautospecifier together with a hint to the implementation that the object so declared will be heavily used. [Note: the hint can be ignored and in most implementations it will be ignored if the address of the object is taken. —end note]
这句话的大概意思就是,register关键字和auto关键字在语义上是一样的(博主注:在C++98标准中auto并没有类型自动推导的作用,它的作用仍然和C语言一样是自动存储类型的意思),只是暗示了这个变量可能会被频繁使用,而且regester关键字起不起作用还要看编译器愿不愿意理它,并且大部分情况下都不愿意理它,而且你如果想对register变量取地址,那么这个关键字就一定会不起作用。这样看来即使在旧的C++标准中regester关键字的作用也不是特别大。
实际上,编译器的优化能力是非常强的,会自动帮助做很多优化,大多数时候并没有必要自己加上register关键字来提示编译器来把它优化成寄存器变量,这个优化编译器多数时候会直接做的。因此C++17标准索性删除了register关键字的用法也就不奇怪了(不过这个关键字本身还是被保留了下来)。
总之,在现代C++中,再使用register关键字声明寄存器变量已经是不合标准的了,我们应该避免做这种事情。
In C
与之形成对比的是C语言。C语言的最新标准目前是ISO/IEC 9899:2018。不过这一标准相对于上衣标准C11改动不大,因此我们查看C11标准也不是问题。
其实在C语言中对register变量做的约束就非常大了,不过我们将会看到实际上这也只是语法上的约束,实际的作用可能不如人意。
在6.7.1的第6条正文和注121中这样写道:
6 A declaration of an identifier for an object with storage-class specifier
registersuggests that access to the object be as fast as possible. The extent to which such suggestions are effective is implementation-defined.121)
121) The implementation may treat any
registerdeclaration simply as anautodeclaration. However, whether or not addressable storage is actually used, the address of any part of an object declared with storage-class specifierregistercannot be computed, either explicitly (by use of the unary & operator as discussed in 6.5.3.2) or implicitly (by converting an array name to a pointer as discussed in 6.3.2.1). Thus, the only operator that can be applied to an array declared with storage-class specifierregisterissizeof.
上面的两段话大致意思是,register关键字暗示编译器对这个变量的访问应该尽可能地快.至于这样的 暗示是否有效则是编译器自行决定的。register关键字可以被简单地当作auto对待。然而,不管这个关键字是否有效,我们都不能显式或隐式地取它的地址。
这很好理解,因为我们是要把这个变量声明为寄存器变量的,而寄存器不在内存中,自然是没有地址的。不过这仅仅是一个语法上的 约束,正如之前所说,这个关键字是否实际起作用还需要视编译器而定。在一些情况下,register会退化成auto。
这样看来,register关键字在C语言中可能会有用处,但实际上用处可能并不是那么大。正如我们之前所说,在一些情况下,编译器会自动帮助我们优化这些变量。
结论
总之,现在再去写register关键字已经没有太大的必要了,如果你是在一些编译器版本比较古老的并且不开优化的OJ平台上进行算法竞赛也许是需要节省这样的常数时间的,但是我们在现代的开发中并没有写它的必要,在C++17标准中也不能写上它。
参考文献
- ISO/IEC 9899:2011, Information Technology — Programming languages — C
- ISO/IEC 14882:1998, Programming languages — C++
- ISO/IEC 14882:2017, Programming languages — C++
1万+

被折叠的 条评论
为什么被折叠?



