在 C# 中,整型算术运算在执行过程中可能发生溢出(overflow)。为了控制溢出行为,语言提供了 checked 与 unchecked 两种溢出检查上下文,用于定义运行时或编译期对溢出的处理方式。
本文从机制层面系统说明 C# 的整型溢出行为及其上下文规则。
一、溢出的基本概念
整型类型(如 int、uint、long 等)具有固定的位宽,因此其可表示的数值范围是有限的。当运算结果超出该范围时,即发生溢出。
例如:
-
int.MaxValue + 1超出int表示范围 -
uint.MaxValue + 1超出uint表示范围
不同的溢出检查上下文会导致不同的行为。
二、checked 上下文:启用溢出检测
checked 用于启用溢出检查。在该上下文中:
-
一旦发生整型溢出
-
运行时抛出 System.OverflowException
-
常量表达式溢出会在编译期报错
示例
uint a = uint.MaxValue;
checked
{
Console.WriteLine(a + 1);
}
运行结果:
System.OverflowException
三、unchecked 上下文:禁用溢出检查(默认)
unchecked 用于关闭溢出检查,是默认行为。
在该上下文中:
-
溢出不会抛异常
-
结果按二进制截断(丢弃高位)
-
产生"回绕(wrap-around)"行为
示例
uint a = uint.MaxValue;
unchecked
{
Console.WriteLine(a + 1);
}
输出:
0
四、表达式级别控制
除了语句块,还可以对单个表达式使用 checked / unchecked:
double a = double.MaxValue;
int x = unchecked((int)a);
int y = checked((int)a);
行为说明:
-
unchecked:强制截断转换结果 -
checked:溢出时抛出异常
五、作用域规则(非常关键)
checked / unchecked 采用词法作用域(lexical scope),只影响其内部直接出现的表达式。
示例分析
int Multiply(int a, int b) => a * b;
checked
{
Console.WriteLine(Multiply(int.MaxValue, 2));
}
关键点:
-
checked不会"进入"方法内部 -
方法内部的运算不受外部上下文影响
但:
checked
{
Console.WriteLine(int.MaxValue * 2);
}
这里的乘法表达式直接出现在上下文中,因此会触发溢出检测。
六、不同类型的溢出行为差异
1. 整型类型(核心适用)
包括 int、long、short 等:
-
checked:抛异常 -
unchecked:回绕(mod 2ⁿ)
这是该机制的主要设计目标。
2. 浮点类型
包括:
-
float -
double -
Half
特点:
-
溢出通常变为
PositiveInfinity或NegativeInfinity -
checked / unchecked对结果影响有限
3. decimal 类型
特点:
-
不使用回绕语义
-
溢出始终抛异常(与上下文无关)
七、受影响的操作范围
溢出检查上下文主要影响:
1. 算术运算
-
+ -
- -
* -
/ -
++、--
2. 类型转换
-
整型之间转换
-
浮点 → 整型转换
⚠ 特例:
decimal → 整型超范围始终抛异常
八、默认行为规则
如果未显式指定:
-
非常量表达式:
- 默认
unchecked
- 默认
-
常量表达式:
-
默认
checked -
溢出直接编译错误
-
示例:
int x = int.MaxValue + 1; // 编译错误
可显式关闭:
int x = unchecked(int.MaxValue + 1);
九、用户自定义运算符
对于用户定义类型:
-
可自定义运算符行为
-
可实现 checked / unchecked 版本
-
不保证一定遵循内置整型语义
因此:
checked并不强制所有用户运算符必须抛 OverflowException
十、设计意义与实践建议
✔ 使用建议
-
金融 / 精确计算:
- 使用
checked或decimal
- 使用
-
性能敏感系统:
- 可使用
unchecked
- 可使用
-
库设计:
- 明确溢出语义(避免依赖默认行为)
十一、总结
C# 的整型溢出机制本质上是一个语义可控的数值安全模型:
-
checked:安全优先(异常检测) -
unchecked:性能优先(回绕语义) -
默认行为:混合策略(表达式类型决定)
通过这两种上下文,开发者可以在安全性与性能之间进行明确权衡,避免隐式溢出带来的逻辑错误。