减去uint和int以及常量折叠

本文关键字:常量 折叠 int uint 减去 | 更新日期: 2023-09-27 18:26:33

基于这个有趣的问题:添加int和uint,并像Nicholas Carey的回答中提到的那样玩弄常量折叠,我偶然发现了编译器的一个看似不一致的行为:

考虑以下代码片段:

int i = 1;
uint j = 2;
var k = i - j;

在这里,编译器正确地将k解析为long。正如前面提到的问题的答案中所解释的那样,这种特定行为在规范中得到了很好的定义。

令我惊讶的是,当处理文字常量常量

const int i = 1;
const uint j = 2;
var k = i - j; //Compile time error: The operation overflows at compile time in checked mode.
k = 1 - 2u; //Compile time error: The operation overflows at compile time in checked mode.

在这种情况下CCD_ 3被解析为CCD_。

在处理常量时,行为不同是有原因的吗?还是这是编译器中一个小但不幸的"错误"(缺乏更好的术语)?

减去uint和int以及常量折叠

根据C#规范第5版第6.1.9节,常量表达式只允许以下隐式转换

6.1.9隐式常量表达式转换
隐式常量表达式转换允许以下转换:
*int类型的常量表达式(§7.19)可以转换为sbytebyteshortushortuintulong类型,前提是常量表达式的值在目标类型的范围内
long类型的常量表达式可以转换为ulong类型,前提是常量表达式的值不是负数。

注意,long不在int转换的列表中。

另一半的问题是,二进制操作只有少量的数字提升:

(来自第7.3.6.2节二进制数字促销):

  • 如果其中一个操作数是十进制类型,则另一个操作数会转换为十进制类型,或者如果另一个运算数是浮点或双精度类型,则会发生绑定时间错误
  • 否则,如果任一操作数的类型为double,则另一个操作数将转换为double类型
  • 否则,如果任一操作数的类型为float,则另一个操作数将转换为float类型
  • 否则,如果其中一个操作数的类型为ulong,则另一个操作数会转换为ulong类型;如果另一个操作数的类型是sbyte、short、int或long,则会发生绑定时间错误
  • 否则,如果任一操作数的类型为long,则另一个操作数将转换为long类型
  • 否则,如果任一操作数的类型为uint,而另一个操作数类型为sbyte、short或int,则两个操作数都将转换为long类型
  • 否则,如果任一操作数的类型为uint,则另一个操作数将转换为uint类型
  • 否则,两个操作数都将转换为int类型

记住:常数禁止intlong的转换,这意味着两个参数都被提升为uint s。

点击此处查看答案

问题是您使用的是const。

在运行时,当存在常量时,行为与文字完全相同,或者就好像你在代码中对这些数字进行了硬编码一样,所以由于数字是1和2,它强制转换为Uint32,因为1在Uint32的范围内。然后,当您尝试用uint32减去1-2时,它会溢出,因为1u-2u=+4294967295(0xFFFFFFFF)。

编译器可以查看垃圾,并与其他变量不同地解释它们。由于const永远不会改变,所以它可以做出其他情况下无法做出的保证。在这种情况下,它可以保证1在uint的范围内,否则它可以隐式地强制转换它。在正常情况下(没有const),它不能做出这种保证,

带符号int的范围从-2147483648(0x80000000)到+214783647(0x7FFFFFFF)。

无符号int的范围从0(0x00000000)到+4294967295(0xFFFFFFFF)。

这个故事的寓意是,在混合const和var时要小心,你可能会得到一些你意想不到的东西。