checked 和 unchecked 基元类型操作
对基元类型执行的许多算术运算都可能造成溢出:
Byte b = ;
b = (Byte) (b + ); // b 现在包含 44(或者十六进制值 2C)
重要提示:执行上述算术运算时,第一步要求所有操作数都扩大为 32 位值(或者 64 位值,如果任何操作数需要超过 32 位来表示的话)。所以, b 和 200(这两个值都不超过 32 位) 首先转换成 32 位值,然后加到一起。结果是一个 32 位值(十进制 300,或十六进制 12C)。该值在存回变量 b 之前,必须转型为一个 Byte。
C#不会隐式执行这个转型操作,这正是第二行代码需要强制转换为 Byte 的原因。
在大多数编程情形中,这种静悄悄发生的溢出是我们不希望的。如果没有检测到这种溢出,会导致应用程序行为失常。但在极少数编程情形中,比如计算一个哈希值或者校验和,这种溢出不仅是可以接受的,还是我们希望的。
不同语言以不同方式处理溢出。 C 和 C++不将溢出视为错误,并允许值回滚( wrap) 39;应用程序将“若无其事”地运行。相反, Microsoft Visual Basic 总是将溢出视为错误,并会在检测到溢出时抛出一个异常。
CLR 提供了一些特殊的 IL 指令,允许编译器选择它认为最恰当的行为。 CLR 有一个 add 指令,作用是将两个值加到一起,但不执行溢出检查。 CLR 还有一个 add.ovf 指令,作用也是将两个值加到一起,但会在发生溢出时抛出一个 System.OverflowException 异常。除了用于加法运算的这两个 IL 指令, CLR 还为减、乘和数据转换提供了类似的 IL 指令,分别是 sub/sub.ovf, mul/mul.ovf 和 conv/conv.ovf。
C#允许程序员自己决定如何处理溢出。溢出检查默认是关闭的。也就是说,编译器在生成 IL 代码时,会自动使用加、减、乘以及转换指令的不含溢出检查的版本。这样的结果是代码能够更快地运行——但是,开发人员必须保证不会发生溢出,或者他们的代码能预见到这些溢出。
让 C#编译器控制溢出的一个办法是使用 /checked+编译器开关。这个开关指示编译器在生成代码时,使用加、减、乘和转换指令的溢出检查版本。这样生成的代码在执行时会稍慢一些,因为 CLR 会检查这些运算,判断是否会发生溢出。如果发生溢出, CLR 会抛出一个 OverflowException 异常。
除了全局性地打开或关闭溢出检查,程序员还可在代码的特定区域控制溢出检查。 C#通过提供 checked和 unchecked 操作符来实现这种灵活性。 下面是一个使用了 unchecked 操作符的例子:
UInt32 invalid = unchecked((UInt32) (-)); // OK
下例则使用了 checked 操作符:
Byte b = ;
b = checked((Byte) (b + )); // 抛出 OverflowException 异常
在这个例子中, b 和 200 首先转换成 32 位值,然后加到一起,结果是 300。然后,因为显式转型的存在, 300 被转换成一个 Byte,这造成一个OverflowException 异常。如果 Byte 是在 checked 操作符的外部转
型的,则不会发生异常:
b = (Byte) checked(b + ); // b 包含 44;不会抛出 OverflowException 异常
除了 checked 和 unchecked 操作符, C#还支持 checked 和 unchecked 语句,它们造成一个块中的所有表达式都进行或不进行溢出检查:
checked { // 开始一个 checked 块
Byte b = ;
b = (Byte) (b + ); // 该表达式会进行溢出检查
} // 结束一个 checked
事实上,如果使用了一个 checked 语句块,就可以将+=操作符用于 Byte,从而稍微简化一下代码:
checked { // 开始一个 checked 块
Byte b = ;
b += ; // 该表达式会进行溢出检查
}
重要提示:由于 checked 操作符和 checked 语句唯一的作用就是决定生成哪一个版本的加、减、乘和数据转换 IL 指令,所以在一个 checked 操作符或者语句中调用一个方法,不会对该方法造成任何影响。
重要提示:
System.Decimal 类型是一个非常特殊的类型。虽然许多编程语言(包括 C#和 Visual Basic) 都将 Decimal视为一个基元类型,但 CLR 则不然。这意味着 CLR 没有相应的 IL 指令来决定如何处理一个 Decimal 值。在.NETFramework SDK 文档中查看 Decimal 类型可以看出,它提供了一系列 public static 方法,包括 Add, Subtract,Multiply, Divide 等。除此之外, Decimal 类型还为+, -, *, /等提供了操作符重载方法。
编译使用了 Decimal 值的程序时,编译器会生成代码来调用 Decimal 的成员,并通过这些成员来执行实际的运算。这意味着 Decimal 值的处理速度慢于 CLR 基元类型的值的处理速度。另外,由于没有相应的IL 指令来处理 Decimal 值,所以 checked 和 unchecked 操作符、 语句以及编译器开关都失去了效用。 如果对
Decimal 值执行的运算是不安全的,肯定会抛出一个 OverflowException 异常。
类似地, System.Numerics.BigInteger 类型也在内部使用一个 UInt32 数组来表示一个任意大的整数,它的值没有上限和下限。因此,对 BigInteger 执行的运算永远不会造成 OverflowException 异常。然而,如果值 太 大 , 而 且 没 有 足 够 多 的 内 存 来 改 变 数 组 的 大 小 , 对 BigInteger 的 运 算 可 能 抛 出 一 个OutOfMemoryException 异常。
checked 和 unchecked 基元类型操作的更多相关文章
- 《CLR via C#》读书笔记--基元类型、引用类型和值类型
编程语言的基元类型 编译器直接支持的数据类型称为基元类型.基元类型直接映射到Framework类库中存在的类型.例如:C#中的int直接映射到System.Int32类型.下表给出了C#基元类型与对应 ...
- 【C#进阶系列】05 基元类型、引用类型和值类型
基元类型和FCL类型 FCL类型就是指Int32这种类型,这是CLR支持的类型. 而基元类型就是指int这种类型,这是C#编译器支持的,实际上在编译后,还是会被转为Int32类型. 而且学过C的朋友 ...
- 重温CLR(四)基元类型、引用类型、值类型
编程语言的基元类型 编译器直接支持的数据类型称为基元类型(primitive type).基元类型直接映射到framework类型(fcl)中存在的类型. 下表列出fcl类型 从另一个角度,可以认为C ...
- 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_基元类型(三)
[checked 和 unchecked 基元类型操作] 1.第一种使用方式 UInt32 invalid = )); //OK,不会抛异常 Byte b = ; b = ));//抛出Overflo ...
- .net 基元类型,引用类型和值类型
基元类型(primitive type): 编译器直接支持的数据类型称为基元类型(primitive type). string 与 String: 由于C#中的string (一个关键字)直接映射到 ...
- System.Buffer 以字节数组(Byte[])操作基元类型数据
1. Buffer.ByteLength:计算基元类型数组累计有多少字节组成. 该方法结果等于"基元类型字节长度 * 数组长度" , , }; , , }; , , }; Cons ...
- 《CLR via C#》读书笔记(5)基元类型、引用类型和值类型
5.1 基元类型 编译器直接支持的数据类型称为基元类型(primitive type). 以下4行到吗生成完全相同的IL int a = 0; //最方便的语法 System.Int32 b = 0; ...
- 【CLR Via C#】第5章 基元类型、引用类型、值类型
第二遍看这本书,决定记录一下加深印象. 1,基元类型 什么事基元类型?基元类型是直接映射到FrameWork类库(FCL)中存在的类型,编译器直接支持的数据类型.比如int直接映射到System.In ...
- [CLR via C#]5.1 基元类型
原文:[CLR via C#]5.1 基元类型 某些数据类在开发中非常常用,以至于许多编译器允许代码已简化的语法来操作它们.例如可以使用以下语法来分配一个整数: System.Int32 a = ne ...
随机推荐
- 编辑HTML代码,批量制作博文清单
当积累的博文多了,常想制作一个分类文件夹,方便读者阅读.这能够是一个不轻松且枯燥的工作. 以<关于"IT学子成长指导"专栏及文章文件夹>中文件夹的制作为例.介绍我的&q ...
- Atitit,通过pid获取进程文件路径 java php c#.net版本大总结
Atitit,通过pid获取进程文件路径 java php c#.net版本大总结 1. 通过PID获取进程路径的几种方法2 1.1. GetModuleFileNameEx 想获得进程可执行文件的 ...
- Atitit.文件搜索工具 attilax 总结
Atitit.文件搜索工具 attilax 总结 1. 指定目录按照体积大小精确搜索1 1.1. File Seeker 4.5 版本的可以,3.5版本的不行..1 2. 按照文件内容搜索1 2.1. ...
- linux fedora 14(内核2.6.35.6) PF_RING+libpcap 极速捕获千兆网数据包,不丢包
前面讲到了libpcap 捕获数据包,尤其在千兆网的条件下,大量的丢包,网上搜索好久,大概都是PF_PACKET +MMAP,NAPI,PF_RING之类的方法,我对PF_RING+libpcap进行 ...
- 设计模式中类的关系之实现(Realization)
实现关系是用来描述接口和实现接口的类或者构建结构之间的关系,接口是操作的集合,而这些操作就用于规定类或者构建结构的一种服务. 在接口和类之间的实现关系中,类实现了接口,类中的操作实现了接口中所声明的操 ...
- jQuery.getJSON()方法小记
今天看了下jQquery中的getJSON()方法,做点小结: 原型: jQuery.getJSON( url [, data ] [, success(data, textStatus, jqXHR ...
- coreData笔记
1. CDVehicle *vehicle = (CDVehicle *)[[NSManagedObject alloc] initWithEntity:entity insertIntoMan ...
- iOS 数组的去重(普通的无序的去重和排序好的去重)
本文转载至 http://blog.csdn.net/zhaopenghhhhhh/article/details/24972645 有时需要将NSArray中去除重复的元素,而存在NSArray中的 ...
- CodeIgniter框架——CI组件间信息流走向
组件间信息流的走向图: 实线表示直接函数调用. 这些信息流可以从控制器到视图,也可以从控制器到类库或模型.(模型也能调用视图,但理论上这样做不合适.)相反方向就不能调用,如:视图不能调用控制器.然而, ...
- [转]JBOSS4.3关于java.lang.OutOfMemoryError: PermGen space解决方法
vi /jboss/bin/run.bat 找到: set JAVA_OPTS=%JAVA_OPTS% -Xms128m -Xmx512m -XX:MaxPermSize=256m 改为: set J ...