未定义行为发生在程序即使接下来发生什么事情都会导致未定义行为的情况下。 不过,您提供了以下示例。
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
实际返回时,未定义的行为就会发生。编译器期望这种情况不会发生(因为无论如何都会导致程序执行未定义行为),因此任何事情都可能发生。
然而,让我们考虑另一种情况。假设我们正在进行空值检查,并在空值检查后使用变量。
int putchar(int);
const char *warning;
void lol_null_check(const char *pointer) {
if (!pointer) {
warning = "pointer is null";
}
putchar(*pointer);
}
在这个例子中,很容易注意到
lol_null_check
需要非空指针。给非易失性全局
warning
变量赋值不会导致程序退出或抛出任何异常。
pointer
也是非易失性的,因此它不能在函数中间自动更改其值(如果它这样做,那么行为未定义)。调用
lol_null_check(NULL)
将导致未定义的行为,这可能导致变量未被分配(因为此时已知程序执行未定义的行为)。
然而,未定义的行为意味着程序可以做任何事情。因此,没有阻止未定义的行为回溯时间,并在第一行
int main()
执行之前使您的程序崩溃。这是未定义的行为,它不必有意义。它也可能在键入3之后崩溃,但未定义的行为将返回时间,并在您键入3之前崩溃。而且谁知道,也许未定义的行为会覆盖您的系统RAM,并在您的未定义程序不运行时导致您的系统在2周后崩溃。
const int i = 0; if (i) 5/i;
这里遇到问题。 - MSalters