之间有什么区别
int x=7;
和
register int x=7;
?
我正在使用C ++。
之间有什么区别
int x=7;
和
register int x=7;
?
我正在使用C ++。
register在C和C ++之间,重新打开投票的语义不同。
register int a[1];使用该声明,您将无法对该数组建立索引。如果尝试,您会做UB
Answers:
在2010年存在的C ++中,任何使用关键字“自动”或“注册”的有效程序在语义上都将与删除了这些关键字的程序相同(除非它们出现在字符串宏或其他类似的上下文中)。从这种意义上讲,关键字对于正确编译程序毫无用处。另一方面,关键字可能在某些宏上下文中很有用,以确保对宏的不正确使用将导致编译时错误,而不是生成伪代码。
在C ++ 11和更高版本的语言中,该auto关键字被重新用作充当已初始化对象的伪类型,编译器将自动将其替换为初始化表达式的类型。因此,在C ++ 03中,声明:auto int i=(unsigned char)5;等同int i=5;于在块上下文中使用,并且auto i=(unsigned char)5;违反了约束。在C ++ 11中,与等效auto int i=(unsigned char)5;时auto i=(unsigned char)5;成为约束违例auto unsigned char i=5;。
auto不能简单地省略关键字。也许您可以更新答案。
register已被弃用,并且将有一个建议将其删除以用于C ++ 17。
auto的版本现在用于自动类型推断,但是在此之前,它用于指定您希望变量“自动”存储(因此,我猜是在堆栈上)而不是关键字register(意思是“处理器的寄存器”):
register 是对编译器的提示,建议编译器将该变量存储在处理器寄存器中而不是内存中(例如,而不是堆栈)。
编译器可能会也可能不会遵循该提示。
根据Herb Sutter在“没有的关键字(或其他名称的注释)”中的说法:
寄存器说明符与自动说明符具有相同的语义。
storage-class-specifier语法中的语法之一,也没有定义的语义。合格的编译器会像Clang一样抛出错误。但是,某些实现仍然允许它,或者忽略它(MSVC,ICC)或将其用作优化提示(GCC)。参见open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html。但是我在一点上没有说错:在C ++ 11中已弃用。
根据Herb Sutter的说法,register“与空白完全一样有意义”,并且对C ++程序的语义没有影响。
register在C ++ 11中已弃用。它未使用并在C ++ 17中保留。
该register关键字是有用的:
一个生产系统的示例,其中register需要关键字:
typedef unsigned long long Out;
volatile Out out,tmp;
Out register rax asm("rax");
asm volatile("rdtsc":"=A"(rax));
out=out*tmp+rax;
从C ++ 11开始不推荐使用,并且在C ++ 17中未使用和保留。
考虑一种情况,编译器的优化器有两个变量,被迫将一个变量洒到堆栈上。碰巧两个变量对编译器的权重相同。如果没有区别,编译器将随意溢出其中一个变量。另一方面,register关键字为编译器提供了一个提示,该提示将更频繁地访问该变量。它类似于x86预取指令,但用于编译器优化程序。
显然,register提示类似于用户提供的分支概率提示,并且可以从这些概率提示中推断出来。如果编译器知道某个分支经常被使用,它将把与分支相关的变量保留在寄存器中。因此,我建议更多地关注分支提示,而不要去关注register。理想情况下,您的探查器应该以某种方式与编译器进行通信,并使您不必考虑这种细微差别。
从gcc 9.3开始,使用进行编译-std=c++2a, register生成编译器警告,但它仍然有预期的效果,其行为同样到C的register在尊重Ofast优化标志-没有-O1编译时这个答案。但是,使用clang ++-7会导致编译器错误。因此,是的,register优化仅对没有优化-O标志的标准编译有所不同,但它们是编译器即使使用-O1也会发现的基本优化。
唯一的区别是在C ++中,您可以使用寄存器变量的地址,这意味着仅当您不使用变量的地址或其别名(创建指针)或采用引用时,才进行优化它在代码中的位置(仅在-O0上,因为引用也有一个地址,因为它是堆栈上的const指针,就像使用-Ofast进行编译时可以在堆栈外优化的指针一样,除非它们永远不会出现使用-Ofast在堆栈上,因为与指针不同,它们不能被创建volatile并且它们的地址不能被使用),否则它的行为就像您没有使用过的一样register,并且该值将存储在堆栈中。
在-O0上,另一个区别是const register在gcc C和gcc C ++上的行为不同。在gcc C上,const register其行为类似于register,因为const未在gcc上优化block-scope 。在C语言C上,register不执行任何操作,仅应用const块范围优化。在gcc C上进行了register优化,但const在大范围内没有优化。在gcc C ++上,优化register和const块范围优化结合在一起。
#include <stdio.h> //yes it's C code on C++
int main(void) {
const register int i = 3;
printf("%d", i);
return 0;
}
int i = 3;:
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 3
mov eax, DWORD PTR [rbp-4]
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
leave
ret
register int i = 3;:
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
push rbx
sub rsp, 8
mov ebx, 3
mov esi, ebx
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
mov rbx, QWORD PTR [rbp-8] //callee restoration
leave
ret
const int i = 3;
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 3 //still saves to stack
mov esi, 3 //immediate substitution
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
leave
ret
const register int i = 3;
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
mov esi, 3 //loads straight into esi saving rbx push/pop and extra indirection (because C++ block-scope const is always substituted immediately into the instruction)
mov edi, OFFSET FLAT:.LC0 // can't optimise away because printf only takes const char*
mov eax, 0 //zeroed: https://stackoverflow.com/a/6212755/7194773
call printf
mov eax, 0 //default return value of main is 0
pop rbp //nothing else pushed to stack -- more efficient than leave (rsp == rbp already)
ret
register告诉编译器1)在这种情况下rbx,将本地变量存储在被调用方保存的寄存器中,以及2)如果从未使用过变量的地址,则优化堆栈写操作。const告诉编译器立即替换该值(而不是为其分配寄存器或从内存中加载值),并将本地变量作为默认行为写入堆栈。const register是这些勇敢的优化的结合。这是尽可能的苗条。
另外,在gcc C和C ++上,register似乎自己创建了一个堆栈上的第一个本地堆栈在堆栈上随机的16字节间隙,这不会发生const register。
但是使用-Ofast进行编译;register具有0个优化效果,因为如果可以将其放在寄存器中或立即将其保留,那么它将始终存在,如果不能,则不会存在;const仍然优化了C和C ++的负载,但是仅在文件范围内; volatile仍然强制值从堆栈中存储和加载。
.LC0:
.string "%d"
main:
//optimises out push and change of rbp
sub rsp, 8 //https://stackoverflow.com/a/40344912/7194773
mov esi, 3
mov edi, OFFSET FLAT:.LC0
xor eax, eax //xor 2 bytes vs 5 for mov eax, 0
call printf
xor eax, eax
add rsp, 8
ret