为什么创建了register关键字?

ayu*_*hgp 44 c c++ language-lawyer c++11

在阅读Herb Sutter撰写的关键词(或其他名称)时,我遇到了以下几点:

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

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

如果关键字auto(可能不是在C++ 11中)并且register没有价值,那么它们为什么被创建和使用?

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

#include<stdio.h>
int main(){
    register int a = 15;
    printf("%d\n%d\n",&a,a);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

为什么上述程序会出错?

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

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

的printf( "%d \n%d \n",&A,A);

以下程序适用于C++.

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

The*_*kis 54

寄存器

在C中,register存储类用作编译器的提示,以表示变量应优先存储在寄存器中.请注意,将register变量存储在实际寄存器中的提示可能会或可能不会受到尊重,但在任何一种情况下,相关限制仍然适用.见C11,6.7.1p6(强调我的):

具有存储类说明符的对象的标识符声明register表明对对象的访问尽可能快.这些建议有效的程度是实施定义的.[脚注121]

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

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

汽车

在C中,auto存储类定义了自动存储的变量,但由于默认情况下函数局部变量auto,因此通常不会使用它.

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

  • 很多年前(80年代末)我看到一个开发人员移植我们的软件; 他花了几个小时在每个函数的前三个变量前面盲目地"注册",之后整个应用程序的运行速度提高了大约30%. (9认同)
  • @LưuVĩnhPhúc在C中总是有`int`.我认为`auto`在BCPL附近,他们把它留在C中,以便从BCPL到C的移植代码更容易 (3认同)
  • auto关键字是因为C的开头没有类型,只有存储类[在哪里使用C auto关键字?](http://stackoverflow.com/q/2192547/995714) (2认同)
  • @ gnasher729:希望他在编译器上提交一个功能请求,"请写一个寄存器分配器". (2认同)

Tav*_*nes 26

register 在C中有两个目的:

  • 提示编译器应将变量存储在寄存器中以提高性能.这种用法现在基本上已经过时了.
  • 防止程序员以防止其存储在寄存器中的方式使用变量.这种使用只是有点过时的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;
}
Run Code Online (Sandbox Code Playgroud)

程序员编写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;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 恕我直言,`register`关键字可能非常有用,如果没有完全禁止变量的地址被采用,但允许编译器以类似于"限制"指针的方式处理这样返回的地址.因此,在编译器方便的时候,`sscanf(st,"%d",&value);`可以转换成`{int temp; sscanf(st,"%d",&temp); 值=温度;}`; 如果处理"价值"的好处,就好像它的地址从来没有被取消过一样,就是创造临时性的成本. (2认同)
  • 目前,编译器禁止在寄存器中保存类型为"int"的变量,同时访问类型为"int*"的指针(如果可能)但不确定指针是否标识变量,同样适用于其他类型.如果从未采用变量的地址,则证明类型为"int*"的指针无法访问它是微不足道的.如果地址已经暴露给外部代码,那么这种证据通常要困难得多,如果不是不可能的话. (2认同)
  • 我会注意到`register`在嵌入式编程中仍然具有高度相关性. (2认同)

Yu *_*Hao 10

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

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

§6.7.1存储类说明符

由于register无法获取变量的地址,因此存储类的对象register实际上存在于与其他对象不同的空间中.(函数占用了第三个地址空间.)这使它们成为最佳放置的候选者,这是声明寄存器的通常原因; 但它也使它们成为更积极优化的候选者.