C ++에서 키워드 등록


89

차이점은 무엇입니까

int x=7;

register int x=7;

?

C ++를 사용하고 있습니다.


8
@GMan : ANSI C는 레지스터 객체의 주소를 허용하지 않습니다. 이 제한은 C ++에는 적용되지 않습니다
Brian R. Bondy

1
@Brian : 흠, 당신 말이 맞아요. 지금은 메모에 있지만 (주소를 사용하면 무시 될 수 있음) 필수 사항은 아닙니다. 알아 둘만 한. (글쎄, 일종의. : P)
GManNickG 2010-07-08

8
재 개설 투표 register는 C와 C ++간에 서로 다른 의미를 갖습니다.
CB Bailey

3
결과적으로 C에서는 배열 레지스터를 만들어 배열에서 포인터로 변환하는 것을 금지 register int a[1];할 수 있습니다.이 선언을 사용하면 해당 배열을 인덱싱 할 수 없습니다. 시도하면 UB
Johannes Schaub-litb

2
실제로 저는 재개 점을 결정했습니다. 나는 차이가 있다는 것을 알기 전에 마감하기로 투표했습니다.
GManNickG 2010

답변:


24

2010 년에 존재했던 C ++에서 "auto"또는 "register"키워드를 사용하는 유효한 프로그램은 해당 키워드가 제거 된 프로그램과 의미 상 동일합니다 (문자열 매크로 또는 기타 유사한 컨텍스트에 나타나지 않는 한). 그런 의미에서 키워드는 프로그램을 적절하게 컴파일하는 데 쓸모가 없습니다. 반면에 키워드는 특정 매크로 컨텍스트에서 매크로를 잘못 사용하면 가짜 코드를 생성하는 대신 컴파일 타임 오류가 발생하도록하는 데 유용 할 수 있습니다.

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;.


22
마지막 비트의 예가 유용 할 수 있습니다.
Dennis Zickefoose 2010

14
이 답변은 더 이상 정확하지 않습니다. 2011 년부터 키워드 auto를 간단히 생략 할 수 없습니다 ... 답변을 업데이트 할 수 있습니다.
Walter

2
@Walter : 무엇이 변경되었는지 말씀해 주시겠습니까? 모든 언어 변경 사항을 따르지 않았습니다.
supercat 2014-09-17

2
@supercat, 예, 당분간 register은 더 이상 사용되지 않으며 C ++ 17에서 제거 할 제안이있을 것입니다.
조나단 Wakely

3
en.cppreference.com/w/cpp/language/auto 에 따르면 , post C ++ 11 auto은 이제 자동 유형 추론에 사용되지만 그 전에는 변수가 "자동"으로 저장되도록 지정하는 데 사용되었습니다 ( 따라서 스택 에서) 키워드 register( "프로세서의 레지스터"를 의미 )와는 반대로 :
Guillaume

96

register 메모리 대신 프로세서 레지스터에 해당 변수를 저장하도록 권고하는 컴파일러에 대한 힌트입니다 (예 : 스택 대신).

컴파일러는 그 힌트를 따를 수도 있고 따르지 않을 수도 있습니다.

Herb Sutter에 따르면 "아닌 키워드 (또는 다른 이름의 주석)" :

레지스터 지정자는 자동 지정자와 동일한 의미를 갖습니다.


2
그러나 C ++ 17 이후로 더 이상 사용되지 않고 사용되지 않으며 예약되어 있습니다.
ZachB

@ZachB, 이것은 올바르지 않습니다. register는 C ++ 17에서 예약되어 있지만 여전히 작동 하며 C의 레지스터와 거의 동일하게 작동합니다 .
Lewis Kelsey

@LewisKelsey C ++ 17 사양에서는 사용되지 않고 예약되어 있습니다. 그것은 storage-class-specifier문법의 하나 가 아니며 정의 된 의미가 없습니다. 준수 컴파일러는 Clang과 같은 오류를 발생시킬 수 있습니다. 그럼에도 불구하고 일부 구현에서는 여전히이를 허용하고 무시 (MSVC, ICC)하거나 최적화 힌트 (GCC)로 사용합니다. open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html을 참조하십시오 . 하지만 한 가지 요점을 잘못 언급했습니다. C ++ 11에서는 더 이상 사용되지 않습니다.
ZachB


25

오늘날의 컴파일러에서는 아마 아무것도 없을 것입니다. Is는 원래 더 빠른 액세스를 위해 레지스터에 변수를 배치하는 힌트 였지만 오늘날 대부분의 컴파일러는 그 힌트를 무시하고 스스로 결정합니다.


9

거의 확실하지 않습니다.

registerx많이 사용할 계획 이며 레지스터에 배치해야한다고 생각 하는 컴파일러에 대한 힌트 입니다.

그러나 이제 컴파일러는 평균 (또는 전문가) 프로그래머보다 레지스터에 어떤 값을 배치해야하는지 결정하는 데 훨씬 더 뛰어나므로 컴파일러는 키워드를 무시하고 원하는대로 수행합니다.



7

register키워드는 유용했다 :

  • 인라인 어셈블리.
  • 전문가 C / C ++ 프로그래밍.
  • 캐시 가능한 변수 선언.

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 에서 예약되어 있습니다.


2
그리고 'register'키워드는 스레드가없고 멀티 태스킹이없는 단일 C ++ 프로그램을 실행하는 마이크로 컨트롤러에서만 유용 할 것이라고 덧붙였습니다. C ++ 프로그램은 'register'변수가 특수 CPU 레지스터에서 이동되지 않도록 전체 CPU를 소유해야합니다.
Santiago Villafuerte

@SantiagoVillafuerte 답변을 편집하여 추가 하시겠습니까?
ncomputers

그럴듯하게 들리지만 내 대답이 확실하지 않습니다. 다른 사람이 승인하거나 비 승인 할 수 있도록 댓글로 남기고 싶습니다.
Santiago Villafuerte

1
@SantiagoVillafuerte 이것은 멀티 태스킹 시스템에서 사실이 아닙니다. 애플리케이션이 아닌 OS의 컨텍스트 전환이 레지스터 저장 / 복원을 담당하는 경우입니다. 모든 CPU 명령 후 컨텍스트 전환이 아니기 때문에 레지스터에 항목을 넣는 것은 절대적으로 의미가 있습니다. 여기에있는 다른 답변 (컴파일러는 레지스터 할당에 관한 귀하의 의견에 관심이 없음)이 더 정확합니다.
Cubic

지금까지 보여 드린 예는 실제로 GCC의 명시 적 레지스터 변수 확장 을 사용하는 것입니다. 이는 register스토리지 클래스 지정자와 다르며 GCC에서 계속 지원합니다.
ZachB

2

GCC 9.3로, 사용하여 컴파일 -std=c++2a, register 컴파일러 경고를 생산,하지만 여전히 C의 동일 원하는 효과 및 동작합니다있다 register-O1없이 컴파일 할 때 - Ofast 최적화 플래그의 측면에서 대답. 그러나 clang ++-7을 사용하면 컴파일러 오류가 발생합니다. 예, register최적화는 최적화 -O 플래그가없는 표준 컴파일에서만 차이를 만들지 만 컴파일러가 -O1으로도 알아낼 수있는 기본 최적화입니다.

유일한 차이점은 C ++에서 레지스터 변수의 주소를 사용할 수 있다는 것입니다. 즉, 변수 또는 별칭의 주소 (포인터 생성)를 사용하지 않거나 참조를 사용하지 않는 경우에만 최적화가 발생합니다. 단지의 코드 (에서 그것의 - O0 때문에 참조 또한, 주소를 가지고 있기 때문에 그것이 스택에 const를 포인터의 그들이 것을 제외하고 -Ofast를 사용하여 컴파일하는 경우 포인터처럼 스택에서 최적화 할 수 있습니다,,, 결코 나타나지 않습니다 -Ofast를 사용하여 스택에서 포인터와 달리 만들 volatile수없고 주소를 가져올 수 없기 때문입니다. 그렇지 않으면 사용하지 않은 것처럼 동작 register하고 값이 스택에 저장됩니다.

-O0에서 또 다른 차이점은 const registergcc C와 gcc C ++에서 동일하게 작동하지 않는다는 것입니다. gcc C에서는 block-scope 가 gcc에서 최적화되지 않았기 때문에 const register처럼 동작 합니다. clang C 에서는 아무것도하지 않으며 블록 범위 최적화 만 적용됩니다. gcc C에서는 최적화가 적용되지만 블록 범위에서는 최적화가 없습니다. gcc C ++에서는 두 최적화 와 블록 범위 최적화가 결합됩니다.registerconstregisterconstregisterconstregisterconst

#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) 피 호출자 저장 레지스터에 지역 변수를 저장하고,이 경우 rbx2) 변수의 주소를 가져 오지 않으면 스택 쓰기를 최적화하도록 지시 합니다. const컴파일러에게 값을 레지스터에 할당하거나 메모리에서로드하는 대신 즉시 값대체 하고 로컬 변수를 기본 동작으로 스택에 쓰도록 지시합니다. const register이러한 대담한 최적화의 조합입니다. 이것은 얻는 것만 큼 슬림합니다.

또한 gcc C 및 C ++에서 register자체적으로 스택의 첫 번째 로컬에 대해 스택에 임의의 16 바이트 간격 을 만드는 것처럼 보이지만 const register.

그러나 -Ofast를 사용하여 컴파일; register0 최적화 효과가 있습니다. 왜냐하면 레지스터에 넣을 수 있거나 즉시 만들 수 있다면 항상 그렇고 그렇지 않으면 그렇지 않을 것입니다. 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

1

컴파일러의 옵티 마이저에 두 개의 변수가 있고 하나를 스택에 넘겨야하는 경우를 고려하십시오. 두 변수가 컴파일러에 대해 동일한 가중치를 갖게되었습니다. 차이가 없다면 컴파일러는 변수 중 하나를 임의로 유출합니다. 반면에 register키워드는 컴파일러에게 어떤 변수가 더 자주 액세스되는지 힌트를 제공합니다. x86 프리 페치 명령어와 유사하지만 컴파일러 최적화 프로그램 용입니다.

분명히 register힌트는 사용자가 제공 한 분기 확률 힌트와 유사하며 이러한 확률 힌트에서 추론 할 수 있습니다. 컴파일러가 일부 분기가 자주 사용된다는 것을 알고 있으면 분기 관련 변수를 레지스터에 유지합니다. 그래서 나는 분기 힌트에 대해 더 신경을 쓰고 register. 이상적으로는 프로파일 러가 어떻게 든 컴파일러와 통신하고 그러한 뉘앙스에 대해 생각하지 않아도됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.