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

WUJ*_*WUJ 151 java int

限制int是从-2147483648到2147483647.

如果我输入

int i = 2147483648;
Run Code Online (Sandbox Code Playgroud)

那么Eclipse将在"2147483648"下面提示一个红色下划线.

但如果我这样做:

int i = 1024 * 1024 * 1024 * 1024;
Run Code Online (Sandbox Code Playgroud)

它会编译好.

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

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

    }
}
Run Code Online (Sandbox Code Playgroud)

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

ars*_*jii 232

这句话没有错; 你只是将4个数字相乘并将其分配给一个int,恰好就是溢出.这与分配单个文字不同,后者将在编译时进行边界检查.

导致错误的是越界文字,而不是作业:

System.out.println(2147483648);        // error
System.out.println(2147483647 + 1);    // no error
Run Code Online (Sandbox Code Playgroud)

相比之下,long文字可编译良好:

System.out.println(2147483648L);       // no error
Run Code Online (Sandbox Code Playgroud)

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

int i = 1024 * 1024 * 1024 * 1024;
Run Code Online (Sandbox Code Playgroud)

变为:

   0: iconst_0      
   1: istore_1      
Run Code Online (Sandbox Code Playgroud)

请注意,0只需加载和存储result(),不会发生乘法.


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

如果类型的十进制文字int大于2147483648(2 31),或者十进制文字2147483648出现在除一元减号运算符(第15.15.4节)的操作数之外的任何地方,则为编译时错误.

  • 对于乘法,JLS说,如果整数乘法溢出,那么结果就是数学乘积的低阶位,用一些足够大的二进制补码格式表示.结果,如果发生溢出,则结果的符号可能与两个操作数值的数学乘积的符号不同. (11认同)
  • @WouterLievens,溢出_is_通常是一个"异常"条件,如果不是一个彻头彻尾的错误条件.它是有限精度数学的结果,大多数人在进行数学计算时并不直观地期望这种数学.在某些情况下,比如`-1 + 1`,它是无害的; 但对于"1024 ^ 4"而言,它可能会让那些意外结果完全出乎意料的人失望,远远超出他们所期望的那样.我认为应该至少向用户发出警告或注释,而不是默默地忽略它. (4认同)
  • 很好的答案.有些人似乎认为溢出是某种错误或失败,但事实并非如此. (3认同)
  • @ iowatiger08 JLS概述了语言语义,它独立于JVM(因此使用哪个JVM无关紧要). (3认同)

Cru*_*her 43

1024 * 1024 * 1024 * 1024并且2147483648在Java中没有相同的值.

实际上,在Java中,它2147483648 不是一个值(尽管2147483648L是).编译器实际上不知道它是什么,或者如何使用它.所以它发牢骚.

1024是Java中的有效int,有效int乘以另一个有效int,总是有效的int.即使它与您直观期望的值不同,因为计算会溢出.

请考虑以下代码示例:

public static void main(String[] args) {
    int a = 1024;
    int b = a * a * a * a;
}
Run Code Online (Sandbox Code Playgroud)

您是否希望这会产生编译错误?现在变得有点滑.
如果我们放置一个循环3次迭代并在循环中相乘怎么办?

允许编译器进行优化,但它不能在程序执行时更改程序的行为.


有关如何实际处理此案例的一些信息:

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

其他语言或API使用动态数量的位(BigInteger在Java中),引发异常或将值设置为魔术值,例如非数字.

  • 对我来说,你的陈述,"2147483648"并不是一个有价值的东西(尽管`2147483648L`),"**真的**巩固了@arshajii试图制造的观点. (8认同)

Eri*_*ert 16

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

您建议的行为 - 即,当计算生成的值大于可以存储在整数中的最大值时生成诊断消息 - 是一项功能.对于您使用任何功能,必须考虑该功能,被认为是一个好主意,设计,指定,实施,测试,记录并发送给用户.

对于Java,该列表中的一个或多个内容没有发生,因此您没有该功能.我不知道哪一个; 你必须问一个Java设计师.

对于C#来说,所有这些事情确实发生了 - 大约十四年前 - 所以C#中的相应程序自C#1.0以来就产生了错误.

  • 这没有添加任何有用的东西.虽然我不介意对Java进行攻击,但它根本没有回答OP的问题. (45认同)
  • @Seiyria:原来的海报是在问"为什么不呢?" 问题 - "为什么世界不是我认为的应该是这样的?" 不是关于实际代码*的精确技术问题,因此这对StackOverflow来说是一个不好的问题.对模糊和非技术性问题的正确答案是模糊和非技术性这一事实应该不足为奇.我鼓励原始海报提出更好的问题,并避免"为什么不呢?" 的问题. (29认同)
  • @Seiyria:我注意到的接受的答案也没有回答这个含糊不清的非技术问题; 问题是"为什么这不是错误?" 并且接受的答案是"因为它是合法的".这只是*重述问题*; 回答"为什么天空不绿?" "因为它是蓝色的"并没有回答这个问题.但由于这个问题是一个不好的问题,我根本不会责怪回答者; 答案是对一个糟糕问题的完美合理答案. (18认同)
  • Eric先生,这是我发布的问题:"为什么int i = 1024*1024*1024*1024;在eclipse中没有错误报告?".而arshajii的答案正是我的意思(也许更多).有时我无法以非常准确的方式表达任何问题.我认为这就是为什么有些人在Stackoverflow中更准确地修改一些已发布的问题.我想如果我想得到答案"因为它是合法的",我不会发布这个问题.我会尽力发布一些"常规问题",但请理解像我这样的学生而不是那么专业的人.谢谢. (12认同)
  • @WUJ这个答案恕我直言提供了额外的见解和观点.在阅读完所有答案后,我发现这个答案提供了与其他答案一样多的有效性.此外,它还提高了开发人员不是某些软件产品的唯一实现者的意识. (5认同)
  • @Eric:虽然ad-hominem攻击显然是愚蠢的,但这个答案是完全错误的.如果此代码在运行时生成错误,那么在编译时执行成本 - 效益分析以添加编译器逻辑以检测它(或编译器逻辑以检测预编译)将是有用的.但在Java中,这里没有错误.在运行时生成错误的功能不仅需要文档,实现和测试,根据语言规范也是错误的. (4认同)
  • @BenVoigt:我理解的问题不是关于Eclipse,而是为什么Java语言的设计不会导致错误.因为很明显它可能*使它成为语言中的错误 - C#所做的 - 那么它是*不是*Java中的错误的原因必须是我列出的事情之一没有发生.Java团队从未想过它,想到它但不喜欢它,喜欢它但没有指定,实现,测试或发布它.哪些是真的我不知道,但其中一个必须是.这个问题应该提交给Java设计师之一. (4认同)
  • @owlstead:我"抨击"Java的想法当然是荒谬的.指出一个语言设计团队确实实现了一个功能,但另一个语言设计团队没有简单地说明相关事实.您会注意到,在我的回答中,我没有说过Java团队*错误*未能实现此功能.相反,我一再指出*所有设计过程都涉及妥协和成本效益分析*.如果Java团队想到了这个功能并拒绝了它,他们可能会出于明智的原因这样做.这些原因是什么,我不知道 - 问他们. (4认同)
  • @owlstead:我理解为什么人们把东西看作火焰诱饵,而这些东西从来就不是这样的; 事实上,我写了一篇关于密切相关主题的文章,你可以在这里阅读:http://blogs.msdn.com/b/ericlippert/archive/2008/02/20/how-to-not-get-a -question-answered.aspx.在这里,我没有批评,但许多人将我的事实陈述解释为批评.首先,从他们内部,第二,因为全文单向媒体不传达语气或意图; 纠正误解的机制 - 这些评论 - 笨重,耗时且难以使用. (4认同)
  • 我不确定为什么@EricLippert会假设"我不知道为什么"意味着OP认为它应该是这样的.至多它意味着它违背了他对如何根据他所知道的表现的期望,因此他要求澄清.在他的问题中没有任何地方暗示这是Java中的一个缺陷.为什么C#甚至被提及?我理解这是一个展示其他语言如何不同的例子,但它最后只是说明了事实,所以提到它的意图不是很清楚. (2认同)
  • @RahatAhmed:我提到C#的意图是强调我的观点:这个设计决定不是某种技术上的不可能性,而是像所有设计过程一样,选择各种功能的优缺点.特征预算有限,不同的公司对提高编程语言设计的艺术水平有不同的态度.如果问题是"实现此功能有什么好理由?" 我当然可以更详细地回答这个问题,但问题实际上是为什么Java团队做了*不*做某事; 问问他们! (2认同)
  • "你不能只是在没有破坏很多东西的情况下将这个功能添加到Java中" - 这不就是Eric正在制作的吗?这些功能的成本在实施之前必须合理吗?我相信他会是第一个告诉你有很多功能,他希望自己可以添加到C#,但无论出于什么原因都不能.当然没有认真的开发人员认为Java语言设计师需要向C#语言设计师证明自己的合理性; 整个切线对我来说似乎很傻. (2认同)

pie*_*t.t 12

除了arshajii的答案,我想再展示一件事:

不是导致错误的赋值,而是简单地使用文字.当你尝试

long i = 2147483648;
Run Code Online (Sandbox Code Playgroud)

您会注意到它也会导致编译错误,因为右侧仍然是int-literal且超出范围.

所以带有int-values的操作(包括赋值)可能会在没有编译错误的情况下溢出(并且没有运行时错误),但是编译器无法处理那些太大的文字.