Q人们似乎会刻意区分“依赖于实现”、“未定义”和“未指定”的行为。这些分别是什么意思?
A首先,这三者都表示C标准**没有**精确规定特定构造(或使用它的程序)必须做什么的区域。C语言定义中的这种宽松性是传统且故意的:它允许编译器编写者(a)通过将各种构造实现为“硬件如何处理它们”来生成高效代码(另请参阅问题 14.4a),以及(b)忽略(即避免担心为)某些难以精确定义且对编写良好的程序可能没有用的边缘构造生成正确代码(例如,请参阅问题 3.1、3.2 和 3.3 中的代码片段)。
“不被标准精确定义”的这三种变体定义如下:
依赖于实现 (implementation-defined): 实现必须选择一种行为;它不得在编译程序时失败。(使用该构造的程序不一定不正确。)必须记录此选择。标准可以指定一组允许的行为供选择,或者不施加任何特定要求。
未指定 (unspecified): 类似于“依赖于实现”,除了该选择不必被记录。
未定义 (undefined): 任何事情都可能发生;标准不施加任何要求。程序可能无法编译,或者可能执行不正确(崩溃或默默地生成错误结果),或者可能碰巧完全按照程序员的意图执行。
此外,请注意,由于标准对编译器面对未定义行为实例的行为绝对没有要求,因此编译器(更重要的是,任何生成的代码)可以做任何事情。特别是,不能保证只有程序的未定义部分会表现异常,而程序的其余部分会正常执行。认为您可以容忍程序中的未定义行为,并认为其未定义性不会造成伤害,这是危险的;未定义行为的未定义程度可能比您想象的要高。(请参阅问题 3.2 以了解相对简单的示例。)
由于许多人似乎难以理解未定义行为可能达到的深度,因此传统上会用一些引人注目、令人咋舌的例子。未定义意味着,尽管有 9.2 题,printf("%d", j++ <= j);可以输出 42,或者“forty-two”。
如果您有兴趣编写可移植的代码,可以忽略这些区别,因为您通常会希望避免依赖于这三种行为中的任何一种的代码。
(一种不那么受关注的、同样不被精确定义的行为属于第四类,即特定于区域设置的 (locale-specific)。)
参考:ISO Sec. 3.10, Sec. 3.16, Sec. 3.17
Rationale Sec. 1.6