理论上说,你可以将一个处理器寄存器分配给一个全局作用域变量 - 这个寄存器只需在整个程序的生命周期内一直保持分配给该变量。
然而,C编译器通常无法在编译阶段看到整个程序 - C标准是这样编写的,以便每个翻译单元(大致对应于每个.c
文件)可以独立地编译(编译后的对象稍后链接成一个程序)。这就是为什么不允许全局作用域寄存器变量 - 当编译器正在编译b.c
时,它无法知道在a.c
中已经分配了一个全局变量到一个寄存器中(因此b.c
中的函数必须保留该寄存器中的值)。
实际上,GCC允许这样做。在全局范围内以如下形式进行声明:
register int foo asm ("r12");
为全局变量"foo"(在x86_64上)分配寄存器"r12"。这有许多限制,相应的手册页面可能是所有麻烦的全局寄存器变量的最佳参考:
r12
并将其用于其他用途的库函数)。或者可能全局变量在特定代码库中被大量使用,让编译器保留其永久值在那里是有意义的。还要注意寄存器是线程本地的。 - Peter Cordesregister int foo asm("regname")
手动执行此操作。- https://gcc.gnu.org/onlinedocs/gcc/Global-Register-Variables.html 通常对性能来说不是一个好主意,但内核可能会这样做) - Peter Cordes最初,寄存器变量是用来存储在处理器寄存器中的,但全局变量必须存储在数据或BSS段中才能从每个函数访问。今天,编译器不严格解释register
存储类,因此它主要用于兼容性。
register
关键字被忽略是不正确的说法。 - Jens Gustedtregister
关键字的?据我所知,gcc 忽略它(gcc 也忽略 inline
)。 - R.. GitHub STOP HELPING ICEregister int i = 0; int *a = &i;
。另外对于 inline
,您完全错了;它是C99中非常重要和有用的补充。 - Jens Gustedtregister
关键字并不会强制编译器将对象放置在处理器寄存器中,但所有其他语义保持不变。 - Philippregister
关键字的含义与其名称所暗示的不同,现在它与处理环境的寄存器没有多少关系。(尽管它可能曾经是为此选择的。)唯一约束使用使用 register
声明的变量的文本是:
一元 & 运算符的操作数必须是函数指示器、[] 或一元 * 运算符的结果,或指示了一个不是位域且未使用 register 存储类说明符声明的对象的 lvalue。
因此,它对自动变量(在函数中声明的变量)实施了限制,使得获取此类变量的地址是错误的。然后的想法是编译器可以以任何方式表示该变量,例如作为寄存器或立即汇编值等。作为程序员,您承诺不会获取它的地址。通常对于全局变量这并没有太多意义(它们无论如何都有一个地址)。
总结:
register
关键字不被忽略。寄存器这个词在C/C++中被用作请求编译器使用处理器寄存器作为变量。寄存器是CPU使用的一种变量,非常快速地访问,因为它不位于内存(RAM)中。寄存器的使用受体系结构和寄存器本身的大小限制(这意味着有些可能只是像内存指针,其他用于加载特殊的调试值等)。
C/C++使用的调用约定不使用通用寄存器(80x86架构中的EAX、EBX等)保存参数(但返回值存储在EAX中),因此您可以声明一个类似于寄存器的变量,使代码更快。
如果您要求将其设置为全局变量,则要求为所有代码和源文件保留该寄存器。这是不可能的,因此编译器会给出错误,或者将其简单地作为存储在内存中的普通变量。
这种方法对于局部寄存器变量很好,但对于全局寄存器变量来说则没有意义。要使全局寄存器变量有用,程序员通常必须告诉编译器哪个寄存器用于什么变量,并确保在编译所有模块时编译器知道这样的保留,即使那些不使用寄存器的模块也是如此。这对于嵌入式系统特别有用,尤其是对于被中断使用的变量,但是在系统中通常只允许有非常有限的数量(例如2个或更少)这样的变量。
那么我们现在都同意了吗?我们都认为将全局变量作为寄存器变量是一个非常糟糕的想法吗?如果原始的C定义没有禁止它,那可能是因为没有人认为会有人以这种方式实现它 - 尤其是在CISC时代。
此外:现代优化编译器比人类更擅长决定何时将变量保留在寄存器中。如果您的编译器无法做到这一点,那么您真的需要获得更好的编译器。
因为它们在寄存器中。这是一个自相矛盾的说法。