为什么int i = 1024 * 1024 * 1024 * 1024编译没有错误?
int
的极限是从-2147483648到2147483647。
如果我input
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中的一个基本问题,但我不知道为什么第二个变体不会产生错误。
这个说法没有什么不妥; 你只是乘以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
是一个常量expression式 :
int i = 1024 * 1024 * 1024 * 1024;
变为:
0: iconst_0 1: istore_1
请注意,结果( 0
)只是简单的加载和存储,不会发生乘法运算。
从JLS§3.10.1 (感谢@ChrisK提出的评论):
如果
int
types的十进制文字大于2147483648
(2 31 ),或者十进制文字2147483648
出现在非一元减运算符(第15.15.4节 )的操作数以外的任何位置,则是编译时错误。
Java中1024 * 1024 * 1024 * 1024
和2147483648
的值不同。
实际上, 2147483648
在Java中并不是一个值 (尽pipe2147483648L
)。 编译器从字面上不知道它是什么,或者如何使用它。 所以它发牢骚
1024
是一个在Java中有效的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使用dynamic位数(Java中的BigInteger
),引发exception或将值设置为非数值等魔法值。
我不知道为什么第二个变体不会产生错误。
您build议的行为 – 即计算产生的值大于可存储在整数中的最大值时生成诊断消息是一项function 。 对于您使用任何function,必须考虑function,被认为是一个好主意,devise,指定,实施,testing,logging和运送给用户。
对于Java,该列表中的一个或多个事件不会发生,因此您不具备该function。 我不知道哪一个; 你不得不问一个Javadevise师。
对于C#来说,所有这些事情都发生了 – 大约十四年前 – 所以C#中相应的程序自C#1.0以来产生了一个错误。
除了arshajii的回答,我想再展示一件事情:
这不是导致错误的任务 ,而是简单地使用文字 。 当你尝试
long i = 2147483648;
你会注意到它也会导致编译错误,因为右侧仍然是int
-literal和out-range。
所以int
操作(包括赋值)可能会溢出而没有编译错误(也没有运行时错误),但是编译器不能处理那些太大的文字。
答:因为这不是一个错误。
背景:乘法1024 * 1024 * 1024 * 1024
会导致溢出。 溢出通常是一个错误。 发生溢出时,不同的编程语言会产生不同的行为。 例如,C和C ++将它称为未定义的行为,对于有符号的整数,并且行为被定义为无符号整数(取math结果,只要结果是负数就加上UINT_MAX + 1
,只要结果是减去UINT_MAX + 1
大于UINT_MAX
)。
在Java的情况下,如果int
值的操作的结果不在允许的范围内,那么在概念上Java会加或减2 ^ 32,直到结果在允许的范围内。 所以声明是完全合法的,没有错误。 它只是不会产生你可能希望的结果。
你可以肯定地认为这个行为是否有帮助,编译器是否应该给你一个警告。 我个人认为,警告将是非常有用的,但错误将是不正确的,因为它是合法的Java。