为什么 int i = 1024 * 1024 * 1024 * 1024编译没有错误?

int的限制是从 -2147483648到2147483647。

如果我输入

int i = 2147483648;

然后 Eclipse 将在“2147483648”下面提示一个红色下划线。

但如果我这么做:

int i = 1024 * 1024 * 1024 * 1024;

它会编译好的。

public class Test {
public static void main(String[] args) {


int i = 2147483648;                   // error
int j = 1024 * 1024 * 1024 * 1024;    // no error


}
}

也许这是 Java 中的一个基本问题,但我不知道为什么第二个变体没有产生错误。

16315 次浏览

这个语句没有任何问题; ,你只是把4个数字相乘,然后把它赋给一个 int 类型,这里刚好有一个溢出。这与分配单个 字面意思不同,后者将在编译时进行边界检查。

导致错误的是出界 字面意思,而不是 任务:

System.out.println(2147483648);        // error
System.out.println(2147483647 + 1);    // no error

相比之下,使用 long编译就可以了:

System.out.println(2147483648L);       // no error

注意,实际上,结果 仍然是在编译时计算的,因为 1024 * 1024 * 1024 * 1024常量表达式:

int i = 1024 * 1024 * 1024 * 1024;

变成:

   0: iconst_0
1: istore_1

注意,结果(0)只是简单地加载和存储,并且没有发生乘法运算。


来自 JLS 3.10.1(感谢@ChrisK 在评论中提到) :

如果 int类型的十进制文字大于 2147483648(231) ,或者如果十进制文字 2147483648不是作为一元减号操作符(15.15.4)的操作数出现在任何地方,则是编译时错误。

我不知道为什么第二个变体没有产生错误。

你建议的行为,也就是 当计算产生的值大于可以存储在整数中的最大值时产生的诊断消息特写。为了使用任何特性,必须考虑、认为特性是一个好主意、设计、指定、实现、测试、记录并提供给用户。

对于 Java,列表中的一个或多个事情没有发生,因此您没有这个特性。我不知道是哪一个,你得问 Java 设计师。

对于 C # 来说,所有这些事情都发生了——大约在14年前——所以 C # 中相应的程序在 C # 1.0之后产生了一个错误。

除了 arshajii 的回答,我还想说明一件事:

导致错误的不是 任务,而仅仅是使用 字面意思。 当你尝试的时候

long i = 2147483648;

您会注意到它还会导致编译错误,因为右边仍然是 int文本且超出范围。

因此,使用 int值的操作(包括赋值)可能会在没有编译错误(也没有运行时错误)的情况下溢出,但是编译器就是不能处理那些过大的文字。

1024 * 1024 * 1024 * 10242147483648在 Java 中没有相同的值。

实际上,在 Java 中是 2147483648 都算不上价值(尽管 2147483648L是)。编译器实际上不知道它是什么,也不知道如何使用它。所以它在抱怨。

在 Java 中,1024是一个有效的 int,一个有效的 int乘以另一个有效的 int,始终是一个有效的 int。即使它不是你直观预期的那个值,因为计算会溢出。

例子

考虑下面的代码示例:

public static void main(String[] args) {
int a = 1024;
int b = a * a * a * a;
}

您希望这样做会产生编译错误吗。
如果我们将一个循环放入3次迭代,并在循环中进行乘法会怎么样?

编译器被允许进行优化,但是它不能改变程序的行为。


关于如何处理这个案子的一些信息:

在 Java 和许多其他语言中,整数将由固定数量的位组成。不适合给定位数的计算将 溢出; 计算基本上在 Java 中执行 模数2 ^ 32,然后将值转换回 签了整数。

其他语言或 API 使用动态位数(Java 中的 BigInteger) ,引发异常或将值设置为一个神奇的值,如 not-a-number。

因为它不是一个错误。

背景: 增殖 1024 * 1024 * 1024 * 1024会导致溢出。溢出通常是一个 bug。当发生溢出时,不同的编程语言会产生不同的行为。例如,对于有符号整数,C 和 C + + 称之为“未定义行为”,并且这种行为被定义为无符号整数(取数学结果,只要结果是负的就加上 UINT_MAX + 1,只要结果大于 UINT_MAX就减去 UINT_MAX + 1)。

在 Java 中,如果具有 int值的操作的结果不在允许的范围内,那么从概念上讲,Java 将增加或减少2 ^ 32,直到结果在允许的范围内。所以这个声明是完全合法的,没有错误。只是没有产生你所希望的结果。

您当然可以争论这种行为是否有帮助,以及编译器是否应该给您一个警告。我个人认为警告非常有用,但是错误是不正确的,因为它是合法的 Java。