• CLR-基元类型以及溢出检查


    =========(CLR via C#阅读笔记)========

    基元类型(primitive type):

    基元类型也不做过多的解释,举个例子即可清晰的辨别

    在java里曾使用过Sting s="java"; 定义字符串,然后就会觉得很诧异,为啥是大写开头,我写C#,一直都是 string ,int ,double,float等等小写开头,这时候,来了解基元类型方可解惑。

    1  int a=0;
    2  System.Int32 a=0;
    3  int a=new int();
    4  System.Int32 a=new System.Int32();

    以上定义int类型的语法,都是能正确编译与运行的,其中第一种最方便简洁,那么这种方便简洁的int,string,byte..decimal,object等等都称作C#的基元类型,对应FCL类型则为System.Int32(其它依次类推,注意是大写开头)

    以上,基元类型和FCL类型效果完全一致。

     

    基元类型转换可能造成精度或数量级的丢失:

    然后下面谈到,基元类型中的一些转换问题:

    1 int i=5;  //System.Int32
    2 long j=i; //System.Int64 

    上述代码能够正确的完成隐式转换,也支持一些字面值的转换。

    但是将代码调换一下:

    1 1 longi=5;  //System.Int64
    2 2 int j=i; //System.Int32

    这个时候是不安全的转换,因为long 可以容纳更长的数字,如果long对象的值超过了int,那么会发生丢失精度或数量级,这个时候必须使用显示转换。

    但是即使做了显示转换,也无法避免误差的产生:

     

    对数据转换造成误差做了一个小小的测试:

    得出结论,在超过int(-2,147,483,648 到 2,147,483,647)范围后,int会从头在依次计算,比如比范围多1,就会从最大值回到开头,多2,就会往后再数一个

    而浮点型,不论是float还是double都是直接截取整数位不会进行四舍五入(有些语言不是截取)。

     

    checkd和uncheckd基元类操作

    作用:

    一些元算可能会有溢出发生,例如

    1 byte b=100;  //无符号八位值,范围0-255
    2 b=(byte)(b+200); //溢出得到值 (100+200)-255-1=44

     

    如果,你觉得这种溢出是非法的不可接受的,那么可以加上checked:

    几种常见用法:

    checked语句对指定的代码块进行检查:

    1 //unchecked写法亦然,但是表示不对溢出进行检查
    2 checked
    3 {
    4 byte b=100;  //无符号八位值,范围0-255
    5 b=(byte)(b+200); //溢出得到值 (100+200)-255-1=44
    6 }

    需要注意的是,checked操作符仅仅对块里面进行的运算表达式生效,如果你放一个方法进去,是不会有任何效果的

    使用checked操作符:

    1 b=checked((byte)(b+200));
    2 b=(byte)checked(b+200); //两种写法都可以

    这时候,再发生溢出的时候会抛出System.OverflowException: 算术运算导致溢出。

    当然在极少数时候,异常是允许的,甚至是希望的,比如:计算哈希值或者校验和

     

    checked和unchecked到底是啥?

    引申:CLR提供了一些特殊的IL指令,允许编译器选择它认为最恰当的行为。CLR有一个add指令,作用是将两个值相加,但不执行溢出的检查。还有一个add.ovf指令,作用也是将两个值相加,但会发生溢出时抛出System.OverflowException,当然减乘除也有对应方法,分别是sub/sub.ovf,mul/mul.ovf和conv/conv.ovf。而上面说到的checked和unchecked则是C#层面上提供的编译器开关,自然用来指定编译器下对应的指令(上面提到的IL指令)。

    需要注意的是,使用了/checked+(/check-)编译器开关,会使代码执行变得稍慢,因为CLR会进行对应的语句检查。

     

    如何有效的避免对基元类型的不良操作和编码:

    1.  尽量使用有符合数值类型,这允许编译器检测更多的上溢/下溢错误(有符号意味着区分正负,无符号是没有负数的),也会减少可能的强制类型转换,另外无符号数值不符合CLS(Common  Language Standard)。
    2.  如果代码可能发生你不希望的溢出(比如错误的输入造成的),就把这些代码放到checked块中,并捕捉异常。
    3.  将允许溢出的代码块放到unchecked块中
    4.  没有上述检查操作符的,意味着溢出是bug
    5.  开发环境最好,打开编译器的/checked+开关(生成-高级),尽可能暴露问题,发布时应使用/checked-开关,确保代码更快的运行(当然如果能够对检查要求严格,又能接受带来的性能损失也可以打开开发来编译,可以防止应用程序在包含已损坏的数据的前提下运行)

     

    特别的地方:

    •  System.Decimal虽然在C#中是基元类型,但是在CLR中并不是,意味着没有对应的IL指令,它使用int,float,double,uint等数组来表示范围内的大小,如图:
    •  可以分析出:1.操作符“+”,“-”...对于decimal其实是无效的,但是我们不仅可以对他进行操作符的基本运算,也可以用提供的Add...等方法进行运算,因为它内部其实是对操作符进行了重载,且在运算不安全时是会自动抛出OverFlowException的异常的。2.checked  和 unchecked无效,它无需cheked操作符来检查抛出异常,也无法unchecked阻止异常抛出。
    •  System.Numerics.BigInteger类型类似Decimal,也使用数组来表示任意大的数,但是它如何计算都不会溢出,只有在内存不足以改变数组大小时才会抛出异常OutMemoryException

    上述有更为详细的demo,效果如下:

    Github地址:

    https://github.com/JOJOJOFran/CLR-Via-C--TEST/tree/master/Design_Type/Primitive%20Type

     

    有啥问题欢迎指正!

     

  • 相关阅读:
    决策树(chap3)Machine Learning In Action学习笔记
    AdaBoost-Machine Learning In Action学习笔记
    支持向量机-Machine Learning In Action学习笔记
    Alamofire 4.0 迁移指南
    从VSS到SVN再到Git 记Git的基本操作
    倍杀测量者
    P3166 数三角形
    APIO2014 序列分割(斜率优化好题)
    P3694 邦邦的大合唱站队
    ACwing 298 栅栏(单调队列DP)
  • 原文地址:https://www.cnblogs.com/franhome/p/8831944.html
Copyright © 2020-2023  润新知