정의되지 않은 동작은 다음에 무슨 일이 발생하더라도 프로그램이 정의되지 않은 동작을 유발할 때 발생합니다. 그러나 다음 예제를 제공했습니다.
int num = ReadNumberFromConsole();
if (num == 3) {
PrintToConsole(num);
*((char*)NULL) = 0;
}
컴파일러가의 정의를 알지 PrintToConsole
못하면 if (num == 3)
조건부를 제거 할 수 없습니다 . LongAndCamelCaseStdio.h
다음 선언 이있는 시스템 헤더 가 있다고 가정합니다 PrintToConsole
.
void PrintToConsole(int);
너무 도움이되는 건 없어요 이제이 함수의 실제 정의를 확인하여 공급 업체가 얼마나 사악한 지 (또는 그렇게 사악하지 않은, 정의되지 않은 행동이 더 나쁠 수 있음) 살펴 보겠습니다.
int printf(const char *, ...);
void exit(int);
void PrintToConsole(int num) {
printf("%d\n", num);
exit(0);
}
컴파일러는 실제로 컴파일러가 수행하는 작업을 모르는 임의의 함수가 종료되거나 예외를 발생시킬 수 있다고 가정해야합니다 (C ++의 경우). 호출 *((char*)NULL) = 0;
후 실행이 계속되지 않기 때문에 실행되지 않을 것임을 알 수 있습니다 PrintToConsole
.
정의되지 않은 동작은 PrintToConsole
실제로 돌아올 때 발생 합니다. 컴파일러는 이것이 발생하지 않을 것으로 예상하므로 (이로 인해 프로그램이 정의되지 않은 동작을 실행하게되므로) 모든 일이 발생할 수 있습니다.
그러나 다른 것을 고려해 봅시다. null 검사를하고 있고 null 검사 후에 변수를 사용한다고 가정 해 보겠습니다.
int putchar(int);
const char *warning;
void lol_null_check(const char *pointer) {
if (!pointer) {
warning = "pointer is null";
}
putchar(*pointer);
}
이 경우 lol_null_check
NULL이 아닌 포인터 가 필요 하다는 것을 쉽게 알 수 있습니다. 전역 비 휘발성 warning
변수에 할당하는 것은 프로그램을 종료하거나 예외를 throw 할 수있는 것이 아닙니다. 는 pointer
이 마술 (가 않는 경우, 그것은 정의되지 않은 동작입니다) 함수의 중간에 그 값을 변경할 수 있도록, 또한 비 휘발성이다. 호출 lol_null_check(NULL)
하면 정의되지 않은 동작이 발생하여 변수가 할당되지 않을 수 있습니다 (이 시점에서 프로그램이 정의되지 않은 동작을 실행한다는 사실이 알려져 있기 때문입니다).
그러나 정의되지 않은 동작은 프로그램이 무엇이든 할 수 있음을 의미합니다. 따라서 정의되지 않은 동작이 과거로 돌아가는 것을 막을 수는 없으며 첫 번째 행이 int main()
실행 되기 전에 프로그램이 중단됩니다 . 정의되지 않은 동작이므로 말이되지 않아도됩니다. 3을 입력 한 후 충돌 할 수도 있지만 정의되지 않은 동작은 제때로 돌아가서 3을 입력하기 전에 충돌합니다. 그리고 정의되지 않은 동작이 시스템 RAM을 덮어 쓰고 2 주 후에 시스템이 충돌하게됩니다. 정의되지 않은 프로그램이 실행되지 않는 동안.