Java 是否有像 C++ 那样的未定义行为?

Yve*_*ves 4 c++ java side-effects undefined-behavior sequence-points

未定义的行为和序列点

上面的链接讨论的是 C++ 中的序列点和副作用。

一句话,就是在两个序列点之间,如果我们有多个副作用,副作用的顺序是不确定的。

例如,

int x = 1;
int y = 2;
int z = x++ + y++;
Run Code Online (Sandbox Code Playgroud)

我们可以肯定的是,z等于3后z得到3,x并且y会增加---有两个副作用,所以我们不知道第一个哪一个增加。

此外,上面的链接列出了各种序列点。

我的问题是,Java 有完全相同的情况吗?我的意思是相同种类的序列点和相同的未定义行为?

sup*_*cat 8

与大多数其他流行语言相比,“现代”C 和 C++ 之间的主要区别在于,虽然其他语言允许编译器以未指定的方式在各种极端情况下进行选择,但 C 和 C++ 标准的作者不想将语言限制在可以轻松满足任何行为保证的平台上。

给定一个结构,如:

int blah(int x)
{
  return x+10 > 20 ? x : 0;
}
Run Code Online (Sandbox Code Playgroud)

Java 精确地指定了 x 的所有值的行为,包括那些会导致整数回绕的行为;用于二进制补码机器的早期 C 编译器的设计将产生相同的行为,只是具有不同大小的“int”(16 位、36 位等)的机器会在不同的位置换行。然而,使用其他整数表示的机器可能表现不同。

此外,即使是“传统的”C 编译器也会表现得好像计算是在更长的类型上执行的,这也并不罕见。一些机器有一些使用较长类型操作的指令,使用这些指令并将值保持为较长类型有时比将值截断/包装到“int”范围内更便宜。在这样的机器上,即使对于溢出的 10 以内的值,像上面这样的函数也会产生 x 也就不足为奇了。请注意,Java 试图最小化实现之间的行为差​​异,因此通常甚至不允许这种级别的行为变化。

然而,现代 C 比 Java 更进了一步。它不仅允许编译器可能会任意保留整数值的超额精度,现代编译器给出了上述函数可能会推断出,由于标准将允许编译器做任何事情,如果程序接收到会导致函数来接收大于 INT_MAX-10 的 x 值,编译器应该丢弃任何不相关的代码,如果没有收到这样的输入,这些代码将不起作用。这样做的净效果是整数溢出可以任意方式破坏前面代码的效果。

因此,Java 从 Modern C 的“未定义行为”模型中删除了两个步骤;它严格规定了更多的行为,即使在行为没有严格定义的情况下,实现仍然仅限于从各种可能性中进行选择。除非使用 Unsafe 命名空间中的特性或将 Java 与外部语言联系起来,否则 Java 程序的行为将受到更多限制,即使使用此类构造,Java 程序仍将遵守时间和因果关系定律,而 C 程序可能不会这样做。