У чому різниця між
int x=7;
і
register int x=7;
?
Я використовую С ++.
У чому різниця між
int x=7;
і
register int x=7;
?
Я використовую С ++.
registerмає різну семантику між С і С ++.
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, оскільки посилання також має адресу, оскільки це вказівник const на стеці , який, як і вказівник, може бути оптимізований поза стеком при компіляції за допомогою -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має 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