这个循环将无限继续:
char a = 100;
for(a=100; a>=0;--a)
System.out.println(a);
这是因为在算术运算中,a会被提升为int类型的值,并且从16位的char值扩展为32位,因此它始终保持正数。
这个循环将无限继续:
char a = 100;
for(a=100; a>=0;--a)
System.out.println(a);
这是因为在算术运算中,a会被提升为int类型的值,并且从16位的char值扩展为32位,因此它始终保持正数。
它确实会无限循环,而你所述的原因是接近的。它之所以发生是因为a
不能表示任何不满足a >= 0
的数字--char
是无符号的。在Java中,算术下溢是被定义明确的,但没有指示。请参见规范的相关部分如下。
整数运算符不以任何方式指示溢出或下溢。
这意味着除了比较值之外,没有任何溢出/下溢的指示...例如,如果 a
<= --a
,则表示发生了下溢。
在减法之前,对值1和变量的值执行二进制数字提升(§5.6.2)。如果必要,差异通过缩小原始转换(§5.1.3)和/或转化为类型变量进行封箱(§5.1.7),然后存储。前缀递减表达式的值是变量的值,在新值存储之后。
因此,我们可以看到这里有两个主要步骤:二进制数字提升,然后是缩小的原始转换。
按照以下规则转换一种或两种操作数:应用扩展原始转换(§5.1.2):
- 如果任一操作数为
double
类型,则将另一个操作数转换为double
。- 否则,如果任一操作数为
float
类型,则将另一个操作数转换为float
。- 否则,如果任一操作数为
long
类型,则将另一个操作数转换为long
。- 否则,两个操作数都转换为
int
类型。
我们可以看到递减表达式使用a
作为int
处理,从而执行扩展转换。这使它能够表示值-1。
缩小的原始转换可能会丢失有关数字值总体大小的信息,并且可能会失去精度和范围。
...
将带符号整数缩小为整数类型T只丢弃了除n个最低位之外的所有位,其中n是用于表示类型T的位数。除可能丢失数字值大小的信息外,这还可能导致结果值的符号与输入值的符号不同。
仅保留n个最低位意味着仅保留int
表达式a - 1
的最低16位。由于此处的-1为0b11111111 11111111 11111111 11111111,因此仅保存更低的0b11111111 11111111。由于char
是无符号的,所有这些位都对结果有贡献
注意到这里的问题了吗?本质上,这意味着Java整数算术是模运算;在这种情况下,模数是2^16或65536,因为char
是一个16位数据类型。-1 (mod 65536) ≡ 65535,所以递减将会回绕。
不对。 char
值是无符号的 - 当它们小于 0 时,它们会重新回到 65535。
将 char
替换为 byte
- 然后它就可以工作了。
char
的符号是实现定义的...它只保证能够容纳整个ASCII字符集(0-127),而C99确保它至少可以存储8位。 - obatakuint
对应 signed int
,long
对应 signed long int
,short
对应 signed short int
。之所以会有区别,是因为从技术上讲,plain 的char
、signed char
和unsigned char
都是不同的数据类型。 - obataku正如其他人所说,Java中的char
类型是无符号的,因此a >= 0
将始终为真。当a
达到0并再次递减时,它变成了65535。如果你只是想要刻意违反常规,你可以这样编写你的循环,在101次迭代后终止:
char a = 100;
for(a=100; a<=100;--a)
System.out.println(a);
代码审查者随后可以玩得很开心,把你因编写如此可怕的东西而批得体无完肤。 :)