수색…


비고

유형 한정자는 유형에 대한 추가 의미를 설명하는 키워드입니다. 그것들은 타입 시그니처의 필수 부분입니다. 선언의 최상위 레벨 (식별자에 직접적으로 영향을 미침) 또는 하위 레벨 (포인터에만 해당, 지시 값에 영향을 미침) 모두에 나타날 수 있습니다.

예어 비고
const 최상위 수준에 나타나는 것으로 선언 된 개체의 변형을 방지하거나 (포인터 하위 형식 옆에 나타나는) 가리키는 값의 변형을 방지합니다.
volatile 프로그램 제어 흐름의 결과뿐만 아니라 외부 조건의 결과로 선언 된 객체 (최상위 레벨에서) 또는 뾰족한 값 (포인터 하위 유형에서)이 값을 변경할 수 있음을 컴파일러에 알립니다.
restrict 포인터와 관련된 최적화 힌트. 포인터의 유효 기간 동안 동일한 포인팅 대상 오브젝트에 액세스하는 데 사용되는 다른 포인터가 없음을 선언합니다.

저장 클래스 지정자 ( static , extern , auto , register ), 타입 수정 자 ( signed , unsigned , short , long ) 및 타입 지정자 ( int , char , double 등)에 대한 유형 한정자의 순서는 적용되지 않지만 좋은 습관은 앞서 언급 한 순서대로 그들을 배치하는 것입니다.

static const volatile unsigned long int a = 5; /* good practice */
unsigned volatile long static int const b = 5; /* bad practice */

최상위 자격

/* "a" cannot be mutated by the program but can change as a result of external conditions */
const volatile int a = 5;

/* the const applies to array elements, i.e. "a[0]" cannot be mutated */    
const int arr[] = { 1, 2, 3 };

/* for the lifetime of "ptr", no other pointer could point to the same "int" object */
int *restrict ptr;

포인터 하위 유형 자격

/* "s1" can be mutated, but "*s1" cannot */
const char *s1 = "Hello";

/* neither "s2" (because of top-level const) nor "*s2" can be mutated */
const char *const s2 = "World";

/* "*p" may change its value as a result of external conditions, "**p" and "p" cannot */
char *volatile *p;

/* "q", "*q" and "**q" may change their values as a result of external conditions */
volatile char *volatile *volatile q;

수정 불가능한 (const) 변수

const int a = 0; /* This variable is "unmodifiable", the compiler
                    should throw an error when this variable is changed */
int b = 0; /* This variable is modifiable */

b += 10; /* Changes the value of 'b' */
a += 10; /* Throws a compiler error */

const 자격은 우리가 데이터를 변경할 권리가 없다는 것을 의미합니다. 가치가 우리 뒤에서 바뀔 수 없다는 것을 의미하지는 않습니다.

_Bool doIt(double const* a) {
   double rememberA = *a;
   // do something long and complicated that calls other functions

   return rememberA == *a;
}

다른 호출을 실행하는 동안 *a 가 변경되었을 수 있으므로이 함수는 false 또는 true 반환 할 수 있습니다.

경고


const 자격이있는 변수는 포인터를 사용하여 계속 변경 될 수 있습니다.

const int a = 0;

int *a_ptr = (int*)&a; /* This conversion must be explicitly done with a cast */
*a_ptr += 10;          /* This has undefined behavior */

printf("a = %d\n", a); /* May print: "a = 10" */

그러나 이렇게하면 정의되지 않은 동작으로 이어지는 오류가 발생합니다. 여기에서의 어려움은 간단한 예제에서는 예상대로 동작하지만 코드가 커지면 잘못 될 수 있다는 것입니다.

휘발성 변수

volatile 키워드는 변수의 값이 프로그램 제어 흐름의 결과뿐만 아니라 외부 조건의 결과로 언제든지 변경 될 수 있음을 컴파일러에 알립니다.

컴파일러는 volatile 변수와 관련된 모든 것을 최적화하지 않습니다.

volatile int foo; /* Different ways to declare a volatile variable */
int volatile foo;

volatile uint8_t * pReg; /* Pointers to volatile variable */
uint8_t volatile * pReg;

휘발성 변수를 사용하는 주된 이유는 두 가지입니다.

  • 메모리 매핑 I / O 레지스터가있는 하드웨어와 인터페이스합니다.
  • 프로그램 제어 흐름 (예 : 인터럽트 서비스 루틴) 외부에서 수정 된 변수를 사용할 때,

이 예제를 보자.

int quit = false;

void main() 
{
    ... 
    while (!quit) {
      // Do something that does not modify the quit variable
    } 
    ...
}

void interrupt_handler(void) 
{
  quit = true;
}

컴파일러는 while 루프가 quit 변수를 수정하지 않고 루프를 무한 while (true) 루프로 변환하지 않음을 알 수 있습니다. quit 변수가 SIGINTSIGTERM 의 신호 처리기에 설정되어 있어도 컴파일러는이를 알지 못합니다.

volatile quit 를 선언하면 컴파일러가 루프를 최적화하지 않아 문제가 해결됩니다.

이 예제에서 알 수 있듯이 하드웨어에 액세스 할 때도 같은 문제가 발생합니다.

uint8_t * pReg = (uint8_t *) 0x1717;

// Wait for register to become non-zero 
while (*pReg == 0) { } // Do something else

옵티 마이저의 동작은 변수의 값을 한 번 읽는 것입니다. 값은 항상 같기 때문에 다시 읽지 않아도됩니다. 그래서 우리는 무한 루프로 끝납니다. 컴파일러가 우리가 원하는 것을하도록 강제하기 위해 선언을 다음과 같이 수정합니다.

uint8_t volatile * pReg = (uint8_t volatile *) 0x1717;


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow