无论程序接下来发生什么,程序都会导致未定义行为时,将触发未定义行为。但是,您给出了以下示例。
int num = ReadNumberFromConsole();
if (num == 3) {
PrintToConsole(num);
*((char*)NULL) = 0; //undefined behavior
}
除非编译器知道的定义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
变量不会导致程序退出或引发任何异常。该pointer
也是非易失性的,所以它不能在神奇的功能中改变它的值(如果是这样,这是不确定的行为)。调用lol_null_check(NULL)
将导致未定义的行为,这可能导致未分配变量(因为在这一点上,程序执行未定义行为的事实是已知的)。
但是,未定义的行为意味着程序可以执行任何操作。因此,没有什么可以阻止未定义的行为回到过去,并在int main()
执行第一行之前使程序崩溃。这是未定义的行为,没有必要。在键入3之后,它也可能崩溃,但是未定义的行为会回到过去,甚至在您键入3之前就会崩溃。谁知道,也许未定义的行为会覆盖系统RAM,并在2周后导致系统崩溃,未定义的程序未运行时。