未定义行为的后果不仅仅是产生难以预料的结果,事实上,它甚至会允许程序做任何事。在我们的例子中,将这个奇怪的数值存储在数组 a 的第四个元素中恰好会破坏 main 函数的调用栈,这样,当从 main 函数返回时,它不会正常退出程序,而是会跳转到 C 标准库中用以从用户主目录的文件中获取密码的代码。这可不妙。
C 和 C++ 给出了数百条规则来规避未定义行为,其中大多数是常识,比如不要访问不应该访问的内存、不要让算术运算溢出、不要除以 0,等等。但是编译器并不会强制执行这些规则——它甚至没有义务去“揭露”这些明显的违规行为。事实上,前面的程序在编译时就没有抛出错误或发出警告。作为程序员,你需要打起十二分的精神来避免这种未定义行为。
从既往经验来看,作为程序员,我们在这方面可没有什么好记录。在犹他大学学习期间,研究员 Peng Li 修改了 C 编译器和 C++ 编译器,让它们“翻译”过的程序能报告自己是否执行了某种形式的未定义行为。他发现几乎所有程序都执行过某种未定义行为,包括那些直以坚持高标准而声名卓著的项目。幻想自己就能避开 C 和 C++中的未定义行为,就像幻想只要知道了象棋的规则就能赢得比赛一样。