Java 是否像 C++ 那样具有未定义的行为?
Does Java have undefined behavior like C++ does?
Undefined behavior and sequence points
上面的link说的是C++中的sequence point和side effect
一句话,意思是在两个序列点之间,如果我们有多个副作用,则副作用的顺序是不确定的。
例如,
int x = 1;
int y = 2;
int z = x++ + y++;
可以确定的是z
等于3,z
得到3后,x
和y
会增加---有两个边效果,所以我们不知道哪个先增加。
此外,上面的link列出了各种序列点
我的问题是,Java是否有完全相同的情况?我的意思是相同种类的序列点和相同的未定义行为?
如果没有指定,那么它是未定义的,你没有任何保证。
Java 是一种语言,因此存在规范:所谓的 JLS(Java 语言规范)。
如果未指定某些内容(如 HashMap 值的顺序),则不能期望保持顺序。这种情况下的 JLS 类似于 Javadocs.
相应地,如果指定了 但行为是错误的,那就是一个错误。
你的情况是specified in the JLS, section 15.18 Additive Operators。
单个线程上的表达式计算结果在 Java 语言规范中完全指定。表达式计算中没有未定义的行为(在单个线程上)。
在C/C++中,"undefined behaviour"表示任何事情都有可能发生。如果您将 int z = x++ + y++;
放入您的 C 程序中,编译器可能会决定生成格式化您的硬盘驱动器的代码,并且它仍然符合标准。 Java 也没有。
多线程应用程序中某些构造的行为可能是不确定的(如果没有正确同步)但它也不是完全未定义的 - 很明显(在许多事情中)会发生什么如果您没有正确同步应用程序 - 但您不知道发生了列表中的哪些事情。
还有一些 Java API 如果您不以特定的指定方式调用它们,则它们不会定义它们的结果。它们的行为可能因库中的版本而异,但通常在同一版本中行为是一致的。
与大多数其他流行语言相比,"modern" C 和 C++ 之间的一个主要区别在于,虽然其他语言允许编译器以未指定的方式 select 在各种极端情况行为中,作者C 和 C++ 标准的一部分不想将语言限制在可以轻松满足任何类型的行为保证的平台上。
给定如下结构:
int blah(int x)
{
return x+10 > 20 ? x : 0;
}
Java 精确指定 x 的所有值的行为,包括那些
这会导致整数回绕;早期 C 编译器的设计
补码机器会产生相同的行为,除了机器
具有不同大小的 "int"(16 位、36 位等)会以不同的方式换行
地方。使用其他整数表示的机器可能会表现
然而,不同的是。
此外,即使是 "traditional" C 编译器也并不少见
表现得好像计算是在更长的类型上执行的。一些
机器有一些指令可以使用更长的类型,并使用
这些指令和将值保持为更长的类型有时可能是
比 truncating/wrapping 便宜的值进入 "int" 的范围。在这样的
机器,像上面这样的函数产生就不足为奇了
x 即使对于溢出 10 以内的值。请注意 Java 尝试
最小化实现之间的行为差异,因此不会
通常甚至允许这种程度的行为变化。
然而,现代 C 比 Java 多了一步。它不仅允许
因为编译器可能会任意保持过高的精度
对于整数值,现代编译器给出类似上述的函数可能
推断由于标准将允许编译器做任何事情
如果程序接收到会导致函数接收值的输入
x 大于 INT_MAX-10,编译器应该丢弃任何不相关的代码
如果未收到此类输入,则不会产生任何影响。净效应
这是整数溢出会破坏 preceding 代码的效果
以任意方式。
Java 因此从现代 C 的 "Undefined Behavior" 模型中删除了两个步骤;
它严格规定了更多的行为,即使在行为不是 严格 定义的情况下,实现仍然限于从各种可能性中进行选择。除非使用 Unsafe 命名空间中的功能或将 Java 与外部语言链接,否则 Java 程序的行为将受到更多约束,即使使用此类构造 Java 程序仍将遵守法律以 C 程序可能没有的方式处理时间和因果关系。
Undefined behavior and sequence points
上面的link说的是C++中的sequence point和side effect
一句话,意思是在两个序列点之间,如果我们有多个副作用,则副作用的顺序是不确定的。
例如,
int x = 1;
int y = 2;
int z = x++ + y++;
可以确定的是z
等于3,z
得到3后,x
和y
会增加---有两个边效果,所以我们不知道哪个先增加。
此外,上面的link列出了各种序列点
我的问题是,Java是否有完全相同的情况?我的意思是相同种类的序列点和相同的未定义行为?
如果没有指定,那么它是未定义的,你没有任何保证。
Java 是一种语言,因此存在规范:所谓的 JLS(Java 语言规范)。
如果未指定某些内容(如 HashMap 值的顺序),则不能期望保持顺序。这种情况下的 JLS 类似于 Javadocs.
相应地,如果指定了 但行为是错误的,那就是一个错误。
你的情况是specified in the JLS, section 15.18 Additive Operators。
单个线程上的表达式计算结果在 Java 语言规范中完全指定。表达式计算中没有未定义的行为(在单个线程上)。
在C/C++中,"undefined behaviour"表示任何事情都有可能发生。如果您将 int z = x++ + y++;
放入您的 C 程序中,编译器可能会决定生成格式化您的硬盘驱动器的代码,并且它仍然符合标准。 Java 也没有。
多线程应用程序中某些构造的行为可能是不确定的(如果没有正确同步)但它也不是完全未定义的 - 很明显(在许多事情中)会发生什么如果您没有正确同步应用程序 - 但您不知道发生了列表中的哪些事情。
还有一些 Java API 如果您不以特定的指定方式调用它们,则它们不会定义它们的结果。它们的行为可能因库中的版本而异,但通常在同一版本中行为是一致的。
与大多数其他流行语言相比,"modern" C 和 C++ 之间的一个主要区别在于,虽然其他语言允许编译器以未指定的方式 select 在各种极端情况行为中,作者C 和 C++ 标准的一部分不想将语言限制在可以轻松满足任何类型的行为保证的平台上。
给定如下结构:
int blah(int x)
{
return x+10 > 20 ? x : 0;
}
Java 精确指定 x 的所有值的行为,包括那些 这会导致整数回绕;早期 C 编译器的设计 补码机器会产生相同的行为,除了机器 具有不同大小的 "int"(16 位、36 位等)会以不同的方式换行 地方。使用其他整数表示的机器可能会表现 然而,不同的是。
此外,即使是 "traditional" C 编译器也并不少见 表现得好像计算是在更长的类型上执行的。一些 机器有一些指令可以使用更长的类型,并使用 这些指令和将值保持为更长的类型有时可能是 比 truncating/wrapping 便宜的值进入 "int" 的范围。在这样的 机器,像上面这样的函数产生就不足为奇了 x 即使对于溢出 10 以内的值。请注意 Java 尝试 最小化实现之间的行为差异,因此不会 通常甚至允许这种程度的行为变化。
然而,现代 C 比 Java 多了一步。它不仅允许 因为编译器可能会任意保持过高的精度 对于整数值,现代编译器给出类似上述的函数可能 推断由于标准将允许编译器做任何事情 如果程序接收到会导致函数接收值的输入 x 大于 INT_MAX-10,编译器应该丢弃任何不相关的代码 如果未收到此类输入,则不会产生任何影响。净效应 这是整数溢出会破坏 preceding 代码的效果 以任意方式。
Java 因此从现代 C 的 "Undefined Behavior" 模型中删除了两个步骤; 它严格规定了更多的行为,即使在行为不是 严格 定义的情况下,实现仍然限于从各种可能性中进行选择。除非使用 Unsafe 命名空间中的功能或将 Java 与外部语言链接,否则 Java 程序的行为将受到更多约束,即使使用此类构造 Java 程序仍将遵守法律以 C 程序可能没有的方式处理时间和因果关系。