数学概念上来讲,现代计算机就是“万能图灵机”的一个具体实现。从哲学上来说,以二进制逻辑运算为基础的计算机,其行为必然是确定的。“对于一个确定的输入,其输出是完全确定的。”哲学家和科幻小说作家们统一的观点是:无论如何,如果不为计算机的基础计算模型引入不确定性,人工智能(AI)不可能真正实现。但是,向一切其他问题一样,当我们换个角度考虑“计算机行为的确定性问题”的时候,会得出不同的结论。
通常当我们仅仅掌握一个系统(是系统论的哲学概念,不是某个计算机软件或硬件系统)的部分信息的时候,这个子系统的边界同样是不确定的。现代计算机通常由大量不同的部分组成,每一个部分都是前述的这类子系统,它们的边界是松散的,仅仅是一个大致类型的边界的约定。举一个例子:比如我们可以通过某个功能取得一块确定大小内存,但我们不可能事先知道得到的内存内保存的具体内容。
另一方面,作为“高级程序设计语言”,其对机器底层的表达能力还是有相当的差距的,尤其当某个语言要保持某个概念的“语义”时。语言通过语义表达的概念,是面向程序员的一种抽象,通常没有直接对应的机器底层机制,而是通过比较复杂的机器行为组合来模拟。语言抽象语义与机器底层机制的断层,就会造成另一种形式的不确定性。
最后,我们要认识到语言本身的社会性。语言的发展过程中,通常会有许多不同的实现给出不同的行为。作为 C++ 语言的特例,它还包括许多从 C 语言的各种实现版本遗传来的历史问题。对于这类不同编译器会给出不同解释和行为的概念,C++ 标准的策略是保留它们到特定的编译器来实现,标准中仅仅给出可能的 C++ 实现的行为选择。
这些不确定性自然会被带到我们写的程序中。
C++ 语言支持的一些默认行为,还有可能与机器配置的地理文化环境有关。比如语言文字,时区,地区习惯等等都会影响 C++ 提供的库的相关行为。这些行为对程序来说通常并不是真正不确定的。程序员要编写国际化/本地化的程序,总是要与这些信息打交道的。
C++ 标准明确定义:
1.3.14 well-formed program [defns.well.formed]
a C++ program constructed according to the syntax rules, diagnosable semantic rules, and the One Definition Rule (3.2).
“完备的 C++ 程序”遵循 C++ 的语法规则,可诊断的语义规则,以及“一次定义原则(ODR)”。
1.3.4 ill-formed program [defns.ill.formed]
input to a C++ implementation that is not a well-formed program (1.3.14).
对于 C++ 实现来说,“病态的 C++ 程序”就是除了“完备的 C++ 程序”之外的文本输入。
1.3.12 undefined behavior [defns.undefined]
behavior, such as might arise upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements. Undefined behavior may also be expected when this International Standard omits the description of any explicit definition of behavior. [Note: permissible undefined behavior ranges from ignoring the situation completely with unpredictable result, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed. ]
错误的程序构造和数据会导致“未定义行为”,这些程序构造和数据 C++ 标准未强制规定 C++ 实现针对它们进行处理。当 C++ 标准遗漏任何行为的显定义时,也导致“未定义行为”。【注:可接受的未定义行为包括完全忽略产生不确定结果的情况,或者在编译或程序执行时产生环境特有的,有文档描述的行为(可能同时发布出诊断信息),或者终止编译或执行(并发布诊断信息)。许多错误的程序构造不会造成未定义行为,这样的程序需要被诊断。】
1.3.13 unspecified behavior [defns.unspecified]
behavior, for a well-formed program construct and correct data, that depends on the implementation. The implementation is not required to document which behavior occurs. [Note: usually, the range of possible behaviors is delineated by this International Standard. ]
由完备的程序构造和正确的数据产生的某些随实现而定的行为,被称为“未指明的行为”。C++ 实现并不需要提供这些行为的文档。【注:C++标准通常会列出行为可能的范围。】
1.3.5 implementation-defined behavior [defns.impl.defined]
behavior, for a well-formed program construct and correct data, that depends on the implementation and that each implementation shall document.
由完备的程序构造和正确的数据产生的某些随实现而定的行为,被称为“由实现定义的行为”,C++ 标准要求每个实现都对这些行为提供文档说明。
1.3.7 locale-specific behavior [defns.locale.specific]
behavior that depends on local conventions of nationality, culture, and language that each implementation shall document.
依赖于当地国别,文化和语言习惯的行为,被称为“特定于现场的行为”,C++ 标准要求每个实现都对这些行为提供文档说明。
C++ 的初学者经常会问一些貌似“专业”的问题,比如
int i = 5;
int j = (++i) + (++i) + (++i);
或
int f(int a, int b);
int i = 5;
f(++i, ++i);
这样的问题实在不需要多做考虑,而且应该在实际编程实践中尽量避免。因为它们几乎都是“未指明的行为”或“由实现定义的行为”。另一方面,程序的错误或Bugs,通常是由于“未定义的行为”。