为什么创建注册关键字

Why was the register keyword created?

本文关键字:关键字 注册 创建 为什么      更新时间:2023-10-16

在阅读赫伯·萨特(Herb Sutter)的《不是的关键字》(或另一个名字的评论)时,我遇到了这些行:

没错,一些关键字在语义上等同于空格,一个美化的评论。

我们已经了解了为什么C++语言将关键字视为保留字,并且我们已经看到了两个关键字 - auto和register--对C++程序没有任何语义差异。不要使用它们;无论如何,它们只是空格,并且有更快的方法来键入空格。

如果像auto(也许不在C++11中)和register这样的关键字没有价值,那么为什么要创建和使用它们?

如果在变量之前包含register没有任何区别

#include<stdio.h>
int main(){
register int a = 15;
printf("%dn%dn",&a,a);
return 0;
}

为什么上面的程序会给出错误?

test_register.c:在函数"main"中:

test_register.c:4:2:错误:请求寄存器变量"a"的地址

printf("%d%d",&a,a);

以下程序适用于C++。

#include<iostream>
int main(){
register int a = 15;
std::cout<<&a<<'n'<<a;
return 0;
}

寄存器

在 C 语言中,register存储类用作编译器的提示,以表示变量应优先存储在寄存器中。请注意,在实际寄存器中存储register变量的提示可能会也可能不会被接受,但在这两种情况下,相关限制仍然适用。见C11,6.7.1p6(强调我的):

使用存储类说明符register声明对象的标识符建议尽可能快地访问该对象。这些建议在多大程度上有效,由执行决定。[脚注121]

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

在C++中,它只是一个未使用的保留关键字,但可以合理地假设它是为了与 C 代码的语法兼容性而保留的。

自动

在 C 中,auto存储类定义了一个自动存储变量,但通常不使用它,因为默认情况下auto函数局部变量。

同样,可以合理地假设它最初只是为了语法兼容性而转移到C++,尽管后来它有了自己的含义(类型推断)。

C 语言中的register有两个目的:

  • 向编译器提示变量应存储在寄存器中以提高性能。 这种用法现在基本上已经过时了。
  • 防止程序员以阻止变量存储在寄存器中的方式使用该变量。 这种用法只是IMO有些过时。

这类似于const,它

  • 向编译器提示变量可能存储在只读内存中。
  • 防止程序员写入变量

例如,考虑这个简单函数:

int sum(const int *values, size_t length) {
register int acc = 0;
for (size_t i = 0; i < length; ++i) {
acc += values[i];
}
return acc;
}

程序员编写了register以使累加器远离堆栈,避免每次更新时写入内存。 如果实现更改为如下所示的内容:

// Defined in some other translation unit
void add(int *dest, int src);
int sum(const int *values, size_t length) {
register int acc = 0;
for (size_t i = 0; i < length; ++i) {
add(&acc, values[i]);
}
return acc;
}

acc变量的地址用于add()调用时,该变量不能再存储在寄存器中,因为寄存器没有地址。 因此,编译器会将&acc标记为错误,让您知道您可能通过阻止acc存在于寄存器中而破坏了代码的性能。

这在早期更为重要,当时编译器比较笨,整个函数的变量将存在于一个地方。 如今,变量的大部分生命周期都可以在寄存器中度过,只有在获取其地址时才暂时移动到堆栈上。 也就是说,这段代码:

/* Passed by reference for some reason. */
void debug(const int *value);
int sum(const int *values, size_t length) {
int acc = 0;
for (size_t i = 0; i < length; ++i) {
acc += values[i];
}
debug(&acc);
return acc;
}

会导致acc在旧编译器中整个函数的堆栈上生存。 现代编译器会将acc保存在寄存器中,直到debug()调用之前。

现代 C 代码通常不使用register关键字。

C99 Rationale提供了更多关键字register的上下文:

国际标准的基本原理 — 编程语言 — C

§6.7.1 存储类说明符

由于无法获取register变量的地址,因此存储类的对象register有效地存在于与其他对象不同的空间中。(函数占用第三个地址空间。这使它们成为最佳放置的候选者,这是宣布登记册的通常原因;但这也使它们成为更积极的优化的候选者。