as-if 규칙과 volatile 키워드에 대한 자세한 참조를 추가 할 것 입니다. (이 페이지의 맨 아래에있는 "참조"및 "참조"를 따라 원래 사양을 추적하십시오.하지만 cppreference.com을 읽고 이해하기가 훨씬 더 쉽습니다.)
특히이 섹션을 읽어 주시기 바랍니다.
volatile 객체-유형이 volatile로 한정된 객체, 휘발성 객체의 하위 객체 또는 const-volatile 객체의 변경 가능한 하위 객체입니다. volatile로 한정된 유형의 glvalue 표현식을 통해 이루어진 모든 액세스 (읽기 또는 쓰기 작업, 멤버 함수 호출 등)는 최적화 목적 (즉, 단일 실행 스레드 내에서 휘발성)을 위해 가시적 인 부작용으로 처리됩니다. 액세스는 휘발성 액세스 이전 또는 이후에 순서가 지정된 다른 가시적 부작용으로 최적화되거나 재정렬 될 수 없습니다. 이는 휘발성 객체를 신호 처리기와의 통신에 적합하게 만들지 만 다른 실행 스레드와는 그렇지 않습니다. std :: memory_order를 참조하십시오. ). 비 휘발성 glvalue를 통해 (예 : 비 휘발성 유형에 대한 참조 또는 포인터를 통해) 휘발성 객체를 참조하려고하면 정의되지 않은 동작이 발생합니다.
따라서 volatile 키워드는 특히 glvalues 에서 컴파일러 최적화를 비활성화하는 것 입니다. 여기서 volatile 키워드가 영향을 미칠 수있는 유일한 것은 return x
컴파일러가 나머지 함수로 원하는 모든 작업을 수행 할 수 있다는 것입니다.
컴파일러가 반환을 최적화 할 수있는 정도는이 경우 컴파일러가 x의 액세스를 최적화 할 수있는 정도에 따라 다릅니다 (아무것도 재정렬하지 않고 엄밀히 말하면 반환 표현식을 제거하지 않기 때문입니다. ,하지만 스택에 읽고 쓰는 것이므로 간소화 할 수 있어야합니다.) 그래서 제가 읽을 때 이것은 컴파일러가 최적화 할 수있는 정도의 회색 영역이며 두 가지 방법으로 쉽게 논쟁 할 수 있습니다.
참고 : 이러한 경우 항상 컴파일러가 원하는 / 필요한 것과 반대되는 작업을 수행한다고 가정합니다. 최적화를 비활성화하거나 (적어도이 모듈의 경우) 원하는 동작에 대해보다 정의 된 동작을 찾아야합니다. (이것이 유닛 테스트가 중요한 이유이기도합니다.) 결함이라고 생각되면 C ++ 개발자에게 문제를 제기해야합니다.
이 모든 것은 여전히 읽기가 정말 어렵 기 때문에 내가 관련성이 있다고 생각하는 것을 포함하여 직접 읽을 수 있도록 노력하십시오.
glvalue glvalue 표현식은 lvalue 또는 xvalue입니다.
속성 :
glvalue는 lvalue에서 rvalue로, 배열에서 포인터로 또는 함수에서 포인터로 암시 적으로 변환하는 prvalue로 암시 적으로 변환 될 수 있습니다. glvalue는 다형성 일 수 있습니다. 식별하는 객체의 동적 유형이 반드시 표현식의 정적 유형은 아닙니다. glvalue는 표현식에서 허용하는 불완전한 유형을 가질 수 있습니다.
xvalue 다음 표현식은 xvalue 표현식입니다.
반환 유형이 객체에 대한 rvalue 참조 인 함수 호출 또는 오버로드 된 연산자 표현식 (예 : std :: move (x); a [n], 내장 첨자 표현식. 여기서 하나의 피연산자는 배열 rvalue입니다. am, 객체 표현식의 멤버. 여기서 a는 rvalue이고 m은 비 참조 유형의 비 정적 데이터 멤버입니다. a. * mp, 객체 표현식의 멤버에 대한 포인터. 여기서 a는 rvalue이고 mp는 데이터 멤버에 대한 포인터입니다. ㅏ ? b : c, 일부 b 및 c에 대한 삼항 조건식 (자세한 내용은 정의 참조); static_cast (x)와 같은 객체 유형에 대한 rvalue 참조에 대한 캐스트 표현식 임시 구체화 후 임시 객체를 지정하는 표현식입니다. (C ++ 17 이후) 속성 :
rvalue (아래)와 동일합니다. glvalue (아래)와 동일합니다. 특히, 모든 rvalue와 마찬가지로 xvalue는 rvalue 참조에 바인딩되며 모든 glvalue와 마찬가지로 xvalue는 다형성 일 수 있으며 비 클래스 xvalue는 cv-qualified 일 수 있습니다.
lvalue 다음 표현식은 lvalue 표현식입니다.
std :: cin 또는 std :: endl과 같이 유형에 관계없이 변수, 함수 또는 데이터 멤버의 이름. 변수의 유형이 rvalue 참조 인 경우에도 이름으로 구성된 표현식은 lvalue 표현식입니다. 반환 유형이 lvalue 참조 인 함수 호출 또는 오버로드 된 연산자 표현식 (예 : std :: getline (std :: cin, str), std :: cout << 1, str1 = str2 또는 ++ it); a = b, a + = b, a % = b 및 기타 모든 내장 할당 및 복합 할당 표현식; ++ a 및 --a, 내장 사전 증가 및 사전 감소 표현식; * p, 내장 간접 표현식; a [n] 및 p [n], 내장 첨자 표현식, 단 a가 배열 rvalue (C ++ 11부터) 인 경우 제외. am, 객체 표현식의 멤버 (m이 멤버 열거 자 또는 비 정적 멤버 함수 인 경우 제외) 또는 a는 rvalue이고 m은 비 참조 유형의 비 정적 데이터 멤버입니다. p-> m, 포인터 표현식의 내장 멤버 (m이 멤버 열거 자 또는 비 정적 멤버 함수 인 경우 제외). a. * mp, 객체 표현식의 멤버에 대한 포인터. 여기서 a는 lvalue이고 mp는 데이터 멤버에 대한 포인터입니다. p-> * mp, 포인터 표현식의 멤버에 대한 내장 포인터. 여기서 mp는 데이터 멤버에 대한 포인터입니다. a, b, 내장 쉼표 표현식, 여기서 b는 lvalue입니다. ㅏ ? b : c, 일부 b 및 c에 대한 삼항 조건식 (예 : 둘 다 동일한 유형의 l 값이지만 자세한 내용은 정의 참조) "Hello, world!"와 같은 문자열 리터럴; lvalue 참조 유형에 대한 캐스트 표현식 (예 : static_cast (x); 함수 호출 또는 오버로드 된 연산자 표현식, 반환 유형은 함수에 대한 rvalue 참조입니다. static_cast (x)와 같은 함수 유형에 대한 rvalue 참조에 대한 캐스트 표현식. (C ++ 11 이후) 속성 :
glvalue (아래)와 동일합니다. lvalue의 주소를 사용할 수 있습니다. & ++ i 1
및 & std :: endl은 유효한 표현식입니다. 수정 가능한 lvalue는 내장 할당 및 복합 할당 연산자의 왼쪽 피연산자로 사용될 수 있습니다. lvalue는 lvalue 참조를 초기화하는 데 사용할 수 있습니다. 이렇게하면 새 이름이 식으로 식별되는 개체와 연결됩니다.
as-if 규칙
C ++ 컴파일러는 다음 사항이 적용되는 한 프로그램에 대한 모든 변경을 수행 할 수 있습니다.
1) 모든 시퀀스 포인트에서 모든 휘발성 객체의 값이 안정적입니다 (이전 평가가 완료되고 새로운 평가가 시작되지 않음) (C ++ 11까지) 1) 휘발성 객체에 대한 액세스 (읽기 및 쓰기)는 의미 체계에 따라 엄격하게 발생합니다. 발생하는 표현의. 특히 동일한 스레드의 다른 휘발성 액세스와 관련하여 순서가 변경되지 않습니다. (C ++ 11부터) 2) 프로그램 종료시 파일에 기록 된 데이터는 프로그램이 기록 된대로 실행 된 것과 동일합니다. 3) 프로그램이 입력을 기다리기 전에 대화 형 장치로 전송되는 프롬프트 텍스트가 표시됩니다. 4) ISO C pragma #pragma STDC FENV_ACCESS가 지원되고 ON으로 설정된 경우,
사양을 읽으려면 읽어야 할 사양이라고 생각합니다.
참고 문헌
C11 표준 (ISO / IEC 9899 : 2011) : 6.7.3 유형 한정자 (p : 121-123)
C99 표준 (ISO / IEC 9899 : 1999) : 6.7.3 유형 한정자 (p : 108-110)
C89 / C90 표준 (ISO / IEC 9899 : 1990) : 3.5.3 유형 한정자