В чем разница между
int x=7;
а также
register int x=7;
?
Я использую C ++.
В чем разница между
int x=7;
а также
register int x=7;
?
Я использую C ++.
registerимеет разную семантику в C и C ++.
register int a[1];с этим объявлением вы не можете индексировать этот массив. Если вы попытаетесь, вы сделаете UB
Ответы:
В C ++, существовавшем в 2010 году, любая действительная программа, использующая ключевые слова «авто» или «регистр», будет семантически идентична программе с удаленными этими ключевыми словами (если они не появляются в строковых макросах или других подобных контекстах). В этом смысле ключевые слова бесполезны для правильной компиляции программ. С другой стороны, ключевые слова могут быть полезны в определенных контекстах макросов, чтобы гарантировать, что неправильное использование макроса приведет к ошибке времени компиляции, а не к созданию поддельного кода.
В 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 является подсказкой компилятору, советуя ему хранить эту переменную в регистре процессора вместо памяти (например, вместо стека).
Компилятор может следовать этому совету, а может и не следовать.
По словам Херба Саттера в статье «Ключевые слова, которых нет (или комментарии другого имени)» :
Спецификатор регистра имеет ту же семантику, что и автоматический спецификатор ...
storage-class-specifierв грамматику и не имеет определенной семантики. Соответствующий компилятор может выдать ошибку, как это делает Clang. Тем не менее, некоторые реализации все еще позволяют это и либо игнорируют (MSVC, ICC), либо используют в качестве подсказки по оптимизации (GCC). См. Open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html . Однако в одном я ошибся: в C ++ 11 он устарел.
По словам Херба Саттера , registerон «имеет такое же значение, как пробелы » и не влияет на семантику программы на C ++.
Почти наверняка ничего.
registerэто подсказка компилятору, что вы планируете xмного использовать и что, по вашему мнению, его следует поместить в регистр.
Однако компиляторы теперь намного лучше определяют, какие значения следует помещать в регистры, чем средний (или даже опытный) программист, поэтому компиляторы просто игнорируют ключевое слово и делают то, что хотят.
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спецификатора класса хранения и все еще поддерживается GCC.
Рассмотрим случай, когда оптимизатор компилятора имеет две переменные и вынужден передать одну в стек. Так получилось, что обе переменные имеют для компилятора одинаковый вес. Если разницы нет, компилятор произвольно выбрасывает одну из переменных. С другой стороны, registerключевое слово дает компилятору подсказку, к какой переменной будет обращаться чаще. Она похожа на инструкцию предварительной выборки x86, но для оптимизатора компилятора.
Очевидно, что registerподсказки аналогичны подсказкам вероятности перехода, предоставляемым пользователем, и могут быть выведены из этих подсказок вероятности. Если компилятор знает, что какая-то ветвь выполняется часто, он сохранит связанные с ветвью переменные в регистрах. Поэтому я предлагаю больше заботиться о подсказках веток и забыть о них register. В идеале ваш профилировщик должен как-то связываться с компилятором и избавлять вас даже от мысли о таких нюансах.
В НКУ 9.3, компиляции с использованием -std=c++2a, register производит предупреждения компилятора, но она все еще имеет желаемый эффект и ведет себя идентично Кассиопеян registerпри компиляции без -O1 - Ofast оптимизацию флаги в отношении этого ответа. Однако использование clang ++ - 7 вызывает ошибку компилятора. Так что да, registerоптимизации имеют значение только при стандартной компиляции без флагов оптимизации -O, но это базовые оптимизации, которые компилятор определит даже с -O1.
Единственное отличие состоит в том, что в C ++ вам разрешено использовать адрес регистровой переменной, что означает, что оптимизация происходит только в том случае, если вы не берете адрес переменной или ее псевдонимы (для создания указателя) или не берете ссылку его в коде (только на -O0, потому что ссылка также имеет адрес, потому что это константный указатель в стеке , который, как указатель, может быть оптимизирован вне стека при компиляции с использованием -Ofast, за исключением того, что они никогда не появятся в стеке с помощью -Ofast, потому что, в отличие от указателя, они не могут быть созданы volatileи их адреса не могут быть взяты), в противном случае он будет вести себя так, как вы не использовали register, и значение будет сохранено в стеке.
На -O0 другое отличие состоит в том, что const registerна gcc C и gcc C ++ не работают одинаково. На gcc C const registerведет себя так register, потому что область видимости блока constне оптимизирована на gcc. На clang 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имеет нулевой эффект оптимизации, потому что, если его можно поместить в реестр или сделать немедленно, он всегда будет, а если нет, то не будет; 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