首页 > 代码库 > 第八章 表达式和运算符

第八章 表达式和运算符

表达式和运算符
表达式
字面量
整数字面量
实数字面量
字符字面量
字符串字面量
求值顺序
优先级
结合性
简单算术运算符
求余运算符
关系比较运算符和相等比较运算符
递增运算符和递减运算符
条件逻辑运算符
逻辑运算符
移位运算符
赋值运算符

表达式和运算符

表达式


本章将定义表达式,并描述C#提供的运算符。
运算符是一个符号,它表示返回单个结果的操作。操作数(operand)指作为运算符输入的数据元素。一个运算符会:

  • 将操作数作为输入
  • 执行某个操作
  • 基于该操作返回一个值

表达式是运算符和操作数的字符串。可以作为操作数的结构有:

  • 字面量
  • 常量
  • 变量
  • 方法调用
  • 元素访问器,如数组访问器和索引器
  • 其他表达式

例:下面的表达式,有3个运算符和4个操作数

技术分享
技术分享

字面量


字面量(literal)是源代码中键入的数字或字符串,表示一个指定类型的明确的、固定的值。

例:字面量

class Program
{
    static void Main()
    {
        Console.WriteLine("{0}",1024);//整数字面量
        Console.WriteLine("{0}",3.1416F);//浮点型字面量
        Console.WriteLine("{0}",true);//布尔型字面量
    }
}

对于引用类型变量,字面量null表示变量没有设置为内存中的数据。

整数字面量

例:不同的整数类型

236  //整型
236L //长整型
236U //无符号整型
236UL//无符号长整型

整数字面量还可以写成十六进制(hex)形式

技术分享

技术分享
技术分享

技术分享

实数字面量

实数字面量组成如下:

  • 十进制数字
  • 可选的小数点
  • 可选的指数部分
  • 可选的后缀

例:实数字面量的不同格式

float f1=236F;
double d1=236.712;
double d2=.351;
double d3=6.338e-26;

技术分享

技术分享

技术分享

技术分享

无后缀的实数字面量默认是double类型。

字符字面量

字符字面量可以是下面任意一种:

  • 单个字符
  • 简单转义序列:反斜杠+单个字符
  • 十六进制转义序列:反斜杠+大写或小写x+4个十六进制数
  • Unicode转义序列:反斜杠+大写或小写u+4个十六进制数

例:字符字面量的不同格式

char c1=d;
char c2=\n;
char c3=\x0061;
char c4=\u005a;

一些特殊字符及其编码见下图

技术分享
技术分享

字符串字面量

两种字符串字面量类型:

  • 常规字符串字面量
  • 逐字字符串字面量

常规字符串字面量包含:

  • 字符
  • 简单转义序列
  • 十六进制和Unicode转义序列
string st1="Hi there!";
string st2="Val\t5,val\t10";
string st3="Add\x000ASome\u0007Interest";

逐字字符串以@为前缀,它有以下特征:

  • 逐字字符串与常规字符串区别在于转义字符串不会被求值。在双引号中间的所有内容,包括通常被认为是转义序列的内容,都被严格按字符串中列出的那样打印
  • 逐字字符串的唯一例外是相邻的双引号组,它们被解释为单个双引号字符
string rst1="Hi there!";
string vst1=@"Hi there!";
string rst2="It started,\"Four score and seven...\"";
string vst2=@"It started,""Four score and seven...""";
string rst3="Value 1 \t 5,val2 \t 10";
string vst3=@"Value 1 \t 5,val2 \t 10";
string rst4="C:\\Program Files\\Microsoft\\";
string vst4=@"C:\Program Files\Microsoft\";
string rst5=" Print \x000A Multiple \u000A Lines";
string vst5=@" Print
    Multiple
    Lines";

技术分享

技术分享

编译器让相同的字符串字面量共享堆中同一内存位置以节约内存

求值顺序


表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式最终值发生差别。

优先级

正如小学的先乘除再加减,C#中运算符也有优先级。

技术分享
技术分享

结合性

表达式中运算符优先级不同的,从高到低依次运算。但若是运算符优先级相同怎么办?
当连续运算符有相同优先级时,求值顺序由操作结合性决定。

  • 左结合运算符从左至右
  • 右结合运算符从右至左
  • 除赋值运算符外,其他二元运算符都是左结合
  • 赋值运算符和条件运算符是右结合

技术分享

技术分享

简单算术运算符


技术分享

技术分享

求余运算符


求余运算符(%)用第二个操作数除第一个操作数,并返回余数。
求余运算符是二元左结合运算符。

  • 0%3=0,因为0除3得0余0
  • 2%3=2,因为2除3的0余2
  • 4%3=1,因为4除3得1余1

关系比较运算符和相等比较运算符


它们都是二元左结合运算符,对其操作数进行比较并返回bool值。

技术分享

技术分享

与C和C++不同,在C#中数字不具有布尔意义

int x=5;
if(x)//错,x是int类型,不是布尔型
if(x==5)//对,返回true

比较操作和相等性操作
对于大多数引用类型来说,比较它们的相等性,将只比较它们的引用。

  • 如果引用相等,即它们指向内存中相同对象,相等性为true,否则为false,即使内存中两个分离的对象在所有其他方面完全相等。
  • 这称为浅比较

下图阐明了引用类型的比较

  • 图左边,a和b两者引用相同,返回true
  • 图右边,引用不同,所有即使内容相同,也返回false

技术分享

技术分享

string类型也是引用类型,但它的比较方式不同。比较字符串的相等性,将比较它们的长度和内容(区分大小写)

  • 如果两个字符串长度和内容相等则返回true,即使它们占用不同内存区域
  • 这称为深比较(deep comparison)

将在第15章介绍的委托也是引用类型,并且也使用深比较。比较委托的相等性时,让古国两个委托都是null,或两者的调用列表有相同数目成员,并且调用列表想匹配,则返回true。
比较数值表达式,将比较类型和值。比较enum类型时,比较操作数的实际值。枚举在第13章阐述。

递增运算符和递减运算符


技术分享

技术分享

无论运算符前置还是后置,只影响返回给表达式的值。在语句执行后,最终存放在操作数的变量的值相同

技术分享
技术分享

条件逻辑运算符


技术分享 技术分享

条件逻辑运算符使用“短路”(short circuit)模式操作,意思是,如果计算Expr1之后结果已确定,那么它会跳过Expr2的求值。
例:短路示例

bool bVal;
bVal=(1==2)&&(2==2);
//左侧false,接着&&运算,结果必是false,所以跳过了右侧的运算
bVal=(1==1)||(1==2);
//左侧true,接着是||运算,结果必是true,所以跳过了右侧的运算

因为短路特性,不要在Exp2中放置带有副作用的表达式(比如改变一个值),因为可能不会计算。

bool bVal;int iVal=10;
bVal=(1==2)&&(9==iVal++);//结果:bVal=False,iVal=10;
       ↑             ↑
     False        不会计算

逻辑运算符


技术分享

技术分享

移位运算符


技术分享

技术分享

例:移位运算符示例

  • 操作数14的每个位向左移动3个位置
  • 右边结尾腾出位置用0补充
  • 结果为112

技术分享

技术分享

技术分享

 

技术分享

int a,b,x=14;
a=x<<3;
b=x>>3;
Console.WriteLine("{0}<<3={1}",x,a);
Console.WriteLine("{0}>>3={1}",x,b);

技术分享

 

 

技术分享

赋值运算符


赋值运算符是二元右结合运算符

技术分享

技术分享

复合赋值
复合赋值运算符允许一种速记方法,在某些情况下避免左边的变量在右边重复出现。
复合赋值不仅更短,也易于理解。

x=x+(y-z);
x+=y-z;

第八章 表达式和运算符