关于C和C++的register关键字

关于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++, register is not a storage class specifier.
Rationale: The storage class specifier had no effect in C++.

第C.4.3款:

Change: Removal of register storage-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 register specifier has the same semantics an auto specifier 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 register suggests 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 register declaration simply as an auto declaration. However, whether or not addressable storage is actually used, the address of any part of an object declared with storage-class specifier register cannot 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 specifier register is sizeof.

上面的两段话大致意思是,register关键字暗示编译器对这个变量的访问应该尽可能地快.至于这样的 暗示是否有效则是编译器自行决定的。register关键字可以被简单地当作auto对待。然而,不管这个关键字是否有效,我们都不能显式或隐式地取它的地址。
这很好理解,因为我们是要把这个变量声明为寄存器变量的,而寄存器不在内存中,自然是没有地址的。不过这仅仅是一个语法上的 约束,正如之前所说,这个关键字是否实际起作用还需要视编译器而定。在一些情况下,register会退化成auto
这样看来,register关键字在C语言中可能会有用处,但实际上用处可能并不是那么大。正如我们之前所说,在一些情况下,编译器会自动帮助我们优化这些变量。

结论

总之,现在再去写register关键字已经没有太大的必要了,如果你是在一些编译器版本比较古老的并且不开优化的OJ平台上进行算法竞赛也许是需要节省这样的常数时间的,但是我们在现代的开发中并没有写它的必要,在C++17标准中也不能写上它。

参考文献

  1. ISO/IEC 9899:2011, Information Technology — Programming languages — C
  2. ISO/IEC 14882:1998, Programming languages — C++
  3. ISO/IEC 14882:2017, Programming languages — C++
  • 13
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值