C Language
유형 한정자
수색…
비고
유형 한정자는 유형에 대한 추가 의미를 설명하는 키워드입니다. 그것들은 타입 시그니처의 필수 부분입니다. 선언의 최상위 레벨 (식별자에 직접적으로 영향을 미침) 또는 하위 레벨 (포인터에만 해당, 지시 값에 영향을 미침) 모두에 나타날 수 있습니다.
예어 | 비고 |
---|---|
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
변수가 SIGINT
및 SIGTERM
의 신호 처리기에 설정되어 있어도 컴파일러는이를 알지 못합니다.
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;