关于C#中的算术运算

图片 1

使用中间变量交换两个int型变量的值:

思多雅[天行健] 发布时间:2008.11.09
   
在前面的几个章节中,我们学习了C#的基本概念,这一章,我们来学习C#的类型与变量。
    C#语言的类型被分为三类:数值类型、引用类型和指针类型。
       type:
           value-type
           reference-type
           poin ter-typ e
   
注意:指针类型只能用在不安全代码,我们在后面的章节中会进行专门的讨论。
   
数值类型与引用类型所不同的是,数值类型变量直接含有它们的数据,然而引用类型的变量存储对它们的数据的引用,就是后面要介绍的对象。对于引用类型,可能会出现两个变量引用相同对象的情况,这样对于一个变量的的操作就有可能影响到由其它变量引用的对象。对于数值类型,每个变量都有它们自己对数据的拷贝,这样就不可能出现一个对变量的操作影响到另外一个的情况。
   
C#的类型系统是统一的,这样任何类型的数据都可以被看做对象。C#中的任何类型都直接或间接地从objiect
类类型派生,而object
是所有类型的最基本类类。引用类型的数值被看做通过对象,这些对象通过把数值看做类型对象来简化。数值类型的数值通过包装和解包操作来被当做对象。

谜题33: 循环者遇到了狼人
    请提供一个对i声明,将下面的循环转变为无限循环。
    while (i != 0 && i == -i)
    {
    }

int a = 29;

int b = 10;

a = a+b;

b = a-b;

a = a-b;

-------思多雅[天行健]版权所有,首发太平洋论论坛,转载请注明-------

解惑33: 循环者遇到了狼人
    这仍然是一个循环。在布尔表达式(i != 0 && i ==
-i)中,一元减号操作符作用于i,意味着它的类型必须是数字的:一元减号操作符作用于一个非数字预定义类型操作数是非法的。因此,我们要寻找一个非0的数字类型数值,它等于自己的负值。NaN不能满足这个属性,因为它不等于任何数值,因此,i必须表示一个实际的数字。确定没有任何数字满足这样的属性吗?
   
嗯,没有任何实数具有这种属性,但是没有任何一种C#数字类型能够对实数进行完美建模。浮点数值是用一个符号位、一个被通俗地称为尾数(mantissa)的有效数字以及一个指数来表示的。除了0之外,没有任何浮点数等于其符号位取反之后的值,因此,i的类型必然是整数的。
   
有符号的整数类型使用2的补码算术运算:为了取得一个数值的负值,要对其每一位取反,然后加1,从而得到结果。2的补码算术运算的一个很大优势是,0具有唯一的表示形式。如果要对int数值0取负值,将得到0xffffffff+1,它仍然是0。但是,这也有一个相应的缺点。总共存在偶数个int数值——准确地说有232个,其中一个用来表示0,剩下奇数个int数值来表示正整数和负整数,这意味着正的和负的int数值的数量必然不相等。换句话说,这暗示着至少有一个int数值,其负值不能正确地表示为int数值。
   
事实上,恰恰就有一个这样的int数值,它就是int.MinValue,即-231。它的十六进制表示是0x80000000。其符号位为1,其余所有的位都是0。如果我们对这个值取负值,将得到0x7fffffff+1,也就是0x80000000,即int.MinValue!换句话说,int.MinValue是它自己的负值,long.MinValue也是一样[C#语言规范
7.6.2]。对这两个值取负值将产生溢出,但是C#在整数计算(unchecked上下文)中忽略了溢出。其结果已经阐述清楚了,即使它们并不总是你所期望的。
    下面的声明将使得布尔表达式(i != 0 && i ==
-i)的计算结果为true,从而使循环无限循环下去:
    int i = int.MinValue;
    下面这个也可以:
    long i = long.MinValue;
   
如果你对取模运算很熟悉,那么有必要指出,也可以用代数方法解决这个谜题。C#的int算术运算是实际的算术运算对232取模,因此本谜题需要一个对这种线性全等的非零解决方案:
    i ≡ -i(mod 232)
    在恒等式的两边加i,可以得到:
    2i ≡ 0(mod 232)
    对这种全等的非零解决方案就是i =
231。尽管这个值不能表示成int,但是它和-231是全等的,即与int.MinValue全等。
   
总之,C#使用2的补码的算术运算,是不对称的。对于每一种有符号的整数类型(int、long、sbyte和short),负的数值总是比正的数值多一个,这个多出来的值总是这种类型所能表示的最小数值。对int.MinValue取负值不会改变它的值,long.MinValue也是如此。对short.MinValue取负值并将所产生的int数值转型回short,返回的同样是最初的值(short.MinValue)。对sbyte.MinValue来说,也会产生相似的结果。更一般地讲,千万要当心溢出:就像狼人一样,它是个杀手。    
对语言设计者的教训与谜题26中的教训一样。考虑对某种不会悄悄发生溢出的整数算术运算形式提供语言级的支持。
    (注:在C#的checked上下文中将进行溢出检查[C#语言规范
7.5.12])

 

一、C#的数值类型
   
数值类型既是一个结构类型也是枚举类型。C#提供了一系列预定义结构类型,称为简单类型。简单类型通过保留字指定,并且进一步分成数字类型,整数类型和浮点数类型。
       value-type :
           struct-typ e
           enum-type
       struct-typ e:
           type-name
           simp le-type
       simp le-type :
           numeric-type
           bool
       numeric-type :
           integral-type
           floa ting-point-type
           decimal
       integral-type :
           sbyte
           byte
           short
           ushort
           int
           uint
           long
           ulong
           char
      floa ting-point-type:
         float
         double
      enum-type :
         type-name
    所有数值类型都隐式地从类object
继承。不允许任何类型从数值类型派生,因而数值类型是被默认封闭的。
   
一个数值类型的变量通常包含一个那种类型的数值。不像引用类型,数值类型的数值不能为null
或是引用一个进一步派生类型的变量。
   
对某个数值类型的变量赋值就会创建一个对所赋数值的拷贝,它复制了引用而不是引用所指定的对象。

   
C#解惑总目录

相信大家很容易写出来,但考虑到边界值情况时会有一些有趣的事情。

1.1 默认构造函数
   
所有类型都隐含地声明了一个公共的无参数的构造函数,称为默认构造函数。默认构造函数返回一个初始值为零的实例,为数值类型的默认数值:
  对于所有单独类型,默认数值是由一个零位格式产生的数值:
 
对于sbyte、byte、short、ushort、int、uint、long和ulong,默认的数值为0。
  对于char,默认的数值为’x0000’。
  对于float,默认的数值是0.0f。
  对于double,默认的数值为0.0d。
  对于decimal,默认的数值为0.0m。
  对于bool,默认的数值为false。
  对于一个枚举类型E,默认数值是0。
 
对于结构类型,默认数值是把所有数值类型域设为它们的默认类型并且把所有引用类型域设为null的数值。
    像其它任何构造函数一样,一个数值类型的默认的构造函数用new
操作符调用。在下面的例子中,变量i 和j 都初始化为0。
      class A
      {
         void F() {
            int i = 0;
            int j = new int();
          }
      }
   
如上所示,因为每个数值类型隐含的都有公共无参数构造函数,所以让一个结构类型包含一个外部声明的无参数构造函数是不可能的。一个结构类型可以允许声明一个参数化的构造函数。例如
      struct Point
      {
         int x, y;
public Point(int x, int y) {
             this.x = x;
             this.y = y;
           }
       }
    如果已经给出上面的声明,那么语句
       Point p1 = new Point();
       Point p2 = new Point(0, 0);
    都会创建一个Point,其中x 和y 被初始化为0。

我们知道有一个int.MaxValue和int.MinValue表示int型的最大值和最小值。

1.2 结构类型
   
一个结构类型是一个数值类型,它可以声明构造函数、常数、域、方法、属性、索引、操作符和嵌套类型。结构类型在第11章中描述。

当我们直接定义:int a = int.MaxValue + 1的时候,编译器会提示出错:

1.3 简单类型
   
C#提供了一系列的预定义结构类型,称为简单类型。这些简单类型通过关键词确定,但是这些关键词可以为在System
名称空间中的预定义结构类型关键词起简单的别名,就像下面表中所示。
   关键字                有别名的类型
   sbyte              System.SByte
   byte               System.Byte
   short              System.Int16
   ushort             System.Uint16
   int                System.Int32
   uint               System.Uint32
   long               System.Int64
   ulong              System.Uint64
   char               System.Char
   float              System.Single
   double             System.Double
   bool               System.Boolean
   decimal            System.Decimal
   
一个简单类型和它有别名的结构类型是不可分辨的。换句话说,当写下保留字byte
时和写System.Byte确实没有什么区别,并且用System.Int32 也与用保留字int
相同。
   
因为一个简单类型代表了一个结构类型,所以每个简单类型都有成员。例如,int
有在System.Int32 中声明的成员和从System.Object
中继承的成员,并且下面的语句是允许的:
      int i = int.MaxValue;           // System.Int32.MaxValue
constant
      string s = i.ToString();        // System.Int32.ToString()
instance method
      string t = 123.ToString();      // System.Int32.ToString()
instance method
    注意,整数文字上是int 类型数据,并且同时也是System.Int32
结构类型的数据。
简单类型与其它结构类型,其它结构类型允许包含附加操作符:
  大多数简单类型允许通过使用文字来创建
(§错误!未找到引用源。)。例如,123 是int
类型量,而’a’是字符类型量。C#使得不用对其它结构类型文字进行预定义,而其它结构类型数据基本上是通过那些结构类型的构造函数来创建。
 
当一个表达式的运算符都是简单类型常数时,编译器在编译时就可以对这个表达式进行赋值。这样一个表达式称为常数表达式(§错误!未找到引用源。)。包括其它结构类型定义的操作符的表达式通常意味着运行时赋值。
  通过const
声明,就有可能声明一个简单类型(§10.3)的常数。不可能有其它结构类型的常数,但是static
readonly 域提供了相似的作用。
 
包括简单类型的转换可以参加由其它结构类型定义的转换操作符的赋值,但是用户定义的转换操作符不能参与另外一个用户定义操作符的赋值。

 图片 1

1.4 整数类型
   
C#支持九种整数类型:sbyte、byte、short、ushort、int、uint、long、ulong和char。这些整数类型有下面的大小和数值范围:
  sbyte类型表示有符号的8位整数,数值范围为-128到127。
  byte类型表示无符号8 位整数,数值范围为0 到255。
  short类型表示有符号16位整数,数值范围为-32768 到32767。
  ushort类型表示无符号16位整数,数值范围为0 到65535。
  int类型表示有符号32 位整数,数值范围为–2147483648 到2147483647。
  uint类型表示无符号32 位整数,数值范围为0 到4294967295。
  long类型表示有符号64 位整数,数值范围为–9223372036854775808
到9223372036854775807。
  ulong类型表示无符号64 位整数,数值范围为0 到18446744073709551615。
  char类型表示无符号16位整数,数值范围为0
到65535。char类型的可能数值集符合Unicode字符集。
整数类型一元和二元操作符总是按有符号32 位精度、无符号32 位精度、有符号64
位精度或无符号64位精度进行操作。
  对于一元+和~操作符,操作数被转换为类型T,这里T 是int、uint、long
和ulong 中第一个可以完全代表操作数的所有可能值的类型。操作使用类型T
的精度来实现,而结果的精度也是T。
  对于一元操作符-,操作数被转换为类型T,这里T 是int 和long
中第一个可以完全代表操作数的所有可能值的类型。操作使用类型T
的精度来实现,而结果的精度也是T。一元操作符-不能应用于ulong
类型操作数。
 
对于二元操作符+、–、*、/、%、&、^、|、==、!=、>、<、>=和<=操作符,操作数被转换为类型T,这里T
是int、uint、long 和ulong
中第一个可以完全代表操作数的所有可能值的类型。操作使用类型T
的精度来实现,而结果的精度也是T (或相关操作符bool )。
  对于二元操作符<<和>>操作符,操作数被转换为类型T,这里T
是int、uint、long 和ulong
中第一个可以完全代表操作数的所有可能值的类型。操作使用类型T
的精度来实现,而结果的精度也是T

 

char 类型被分类为一种整数类型,但是它在两点上不同于其它整数类型:
 
没有从其它类型到字符类型的隐含的转换。甚至,即使sbyte、byte和ushort类型的数据完全可以用char类型代表,但是从sbyte、byte和ushort类型到char
的隐含转换也不存在。
*  char
类型的常数必须写成字符文字。字符常量可以只是写成与一个斜杠结合的整数文字。例如,(char)10与’x000A’相同。
   
checked和unchecked操作符和语句用来控制检查整数类型算术操作和转换(§7.5.13)的溢出。在一段checked上下文中,一个溢出产生一个编译时错误或者引起扔出一个OverflowException。在一段unchecked
的上下文里,溢出被忽略并且不需要送到目标类型的任何高端位被丢弃。

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图