在编译时计算

evaluated at compile time

提问人:Run 提问时间:8/4/2023 最后编辑:Guru StronRun 更新时间:8/5/2023 访问量:42

问:

两个例子有什么区别?

示例 1:int x = int.MaxValue + 1; // Compile-time error

例 2;int x = int.MaxValue; x = x + 1; // no Compile-time error

《A Nutshell》一书的作者在谈到第一个例子时说,在编译时计算的表达式总是经过溢出检查的。

我不明白第一个和第二个例子之间的区别。两者不是在编译时评估的,那么两者都应该产生相同的错误吗?

C C#-4.0

评论


答:

1赞 Guru Stron 8/5/2023 #1

两者不是在编译时评估的,那么两者都应该产生相同的错误吗?

不,编译器(据我所知)只计算常量表达式,第二个表达式不是一个。

所以在下文中:x + 1

int x = int.MaxValue; 
x = x + 1; // no Compile-time error

不是常量表达式。即,如果您将其更改为:

const int x = int.MaxValue; 
int x1 = x + 1; 

它将产生编译时错误。

一些规范深入探讨:

来自规范:12.8.19 已检查和未检查的运算符:

当上述操作之一产生的结果太大而无法在目标类型中表示时,执行该操作的上下文将控制结果行为:

  • 在已检查的上下文中,如果操作是常量表达式 (§12.23),则会发生编译时错误。否则,在运行时执行操作时,将引发 a。System.OverflowException

在规范的 12.23 常量表达式部分:

常量表达式中只允许使用以下构造:

  • 文本(包括文本)。null
  • 对类和结构类型成员的引用。const
  • 对枚举类型成员的引用。
  • 对本地常量的引用。
  • 带括号的子表达式,这些子表达式本身是常量表达式。
  • 强制转换表达式。
  • checked和表达式。unchecked
  • nameof表达 式。
  • 预定义的 、 、 和 一元运算符。+!~
  • 预定义的 、 、 、 、 和 二元运算符。+*/%<<>>&|^&&||==!=<><=>=
  • 条件运算符。?:
  • sizeof表达式,前提是 unmanaged-type 是 §23.6.9 中指定的类型之一,其返回常量值。sizeof
  • 默认值表达式,前提是类型是上面列出的类型之一。
-3赞 Jack 8/5/2023 #2

主要区别在于示例 2 是两个表达式。第一个赋值 int x,第二个赋值它。示例 2 可以改写为:

int x = int.MaxValue;
x = x + 1;

作者的观点是,编译器不会检查第二个表达式是否存在整数溢出。

评论

1赞 Guru Stron 8/5/2023
你说的“重写”是什么意思,它已经是那种形式了。
0赞 Jack 8/5/2023
我试图明确指出第二个示例是两个表达式。我本可以使用“重构”来更准确地说。
5赞 D Stanley 8/5/2023 #3

int.MaxValue是一个常量值,因此编译器可以计算并确定它是否超出了 的范围。int.MaxValue + 1int

编译器不够复杂,无法知道 是 ,计算并确定它是否溢出。xint.MaxValuex + 1

编译器不会“计算”第二个 - 它只是将表达式转换为在程序运行时计算的等效 IL 代码(这显然会在运行时溢出)。

如果第一个示例使用了溢出的常量表达式(例如),编译器可以用 IL 中常量表达式的结果替换该表达式。int.MaxValue - 1

这就是“在编译时评估”的全部含义