为什么允许未定义的行为(而不是编译/崩溃)?

时间:2010-05-10 11:39:39

标签: language-agnostic compiler-construction compiler-errors unspecified

我理解编译器/解释器语言扩展的原因,但为什么没有有效定义的行为允许静默失败/做奇怪的事情而不是抛出编译器错误?是因为编译器捕获它们会有额外的困难(不可能或只是耗时)?

P.S。什么语言有未定义的行为,哪些没有?

P.P.S。是否存在未定义行为的实例,这些实例并非不可能/需要很长时间才能在编译中捕获,如果有的话,那么有任何好的理由/借口。

3 个答案:

答案 0 :(得分:4)

在C和C ++等语言中需要未定义行为的概念,因为检测导致它的条件是不可能的或者过于昂贵。以此代码为例:

int * p = new int(0);
// lots of conditional code, somewhere in which we do
int * q = p;
// lots more conditional code, somewhere in which we do
delete p;
// even more conditional code, somewhere in which we do
delete q;

此处指针已被删除两次,导致未查看行为。对于像C或C ++这样的语言来说,检测错误太难了。

答案 1 :(得分:1)

很大程度上因为,为了达到某些目的,这是必要的。例如,C和C ++最初用于编写操作系统,包括设备驱动程序等。为此,他们使用(除其他外)直接访问代表I / O设备的特定硬件位置。阻止访问这些位置会阻止C用于其预期目的(并且C ++专门用于允许所有与C相同的功能)。

另一个因素是指定语言和指定平台之间的一个非常基本的决定。要使用相同的示例,C和C ++都基于有意识地决定将定义限制为语言,并将围绕该语言的平台分开。使用Java和.NET作为几个最明显的例子,有很多替代方案,而是指定整个平台。

这两者都反映了对设计态度的基本差异。设计C的基本原则之一(主要保留在C ++中)是“信任程序员”。虽然从未如此直接地说过,但Java的基本“沙箱”概念是基于你应该信任程序员的想法。

对于哪些语言没有未定义的行为,这是一个肮脏的小秘密:出于所有实际目的,所有都有未定义的行为。一些语言(再次,C和C ++是主要的例子)需要付出相当大的努力来指出哪些行为是未定义的,而许多其他语言或者试图声称它不存在(例如,Java)或者大多数忽略了许多“黑暗的角落” “它出现的地方(例如,Pascal,大多数.NET)。

声称不存在的那些通常会产生最大的问题。例如,Java包含了很多尝试以保证一致的浮点结果的规则。在这个过程中,它们使得无法在相当多的硬件上有效地执行Java - 但是浮点结果仍然不能保证一致。更糟糕的是,他们要求的浮点模型并不完美,所以在某些情况下它会阻止获得最好的结果(或者至少会让你做很多额外的工作来绕过它所要求的)。

值得称赞的是,Sun / Oracle已经(最终)开始注意到这个问题,现在正在开发一个相当不同的浮点模型,应该是一个改进。我不确定它是否已经包含在Java中,但我怀疑,当它/如果存在时,旧模型的代码和新模型的代码之间将存在相当大的“裂痕”。

答案 2 :(得分:0)

因为不同的操作系统运行方式不同(...),你不能只说“在这种情况下崩溃”,因为它可能是操作系统可以做得更好的东西。