C++程序设计基础(上)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.5 表达式

表达式是指由数据和运算符组成,按求值规则,表达一个值的式子。表达式可以很简单,例如,一个常数、一个常量或变量名,也可以很复杂,包含各种运算量、运算符等。C++语言的表达式使用相当灵活,功能很强。按照运算性质,表达式可以分为:算术表达式、逻辑表达式、赋值表达式、条件表达式和逗号表达式。

我们结合表达式的种类,讨论各种运算符的作用和表达式的应用规律。有些运算符将在后续有关章节中出现的时候介绍。

1.5.1 运算符

运算符是以简洁的方式表达对数据操作的符号。C++运算符主要有:

            算术运算符       +  -  *  /  %  ++  --
            关系运算符       >  <  ==  >=  <=  !=
            逻辑运算符       !  &&  ||
            位运算符         <<  >>  ~  |  ^  &
            赋值运算符       = 及扩展的复合运算符
            条件运算符       ?:
            逗号运算符        ,
            指针运算符       *  &
            求字节运算符      sizeof
            强制类型转换符    类型符
            分量运算符       .  ->
            下标运算符       []
            其他             ()  ::  new  delete

运算符又称为操作符。不同的运算符要求不同数量的操作数。由操作符和操作数构成表达式。其中,操作数可以是常量、变量或表达式。根据要求操作数的个数不同,运算符可以分为一元运算符、二元运算符和三元运算符。

① 一元运算符。一元运算符只要求有右操作数,表达式形式为:

            Op  右操作数                   左操作数  Op

其中,Op表示运算符。+,-,!,++都是一元运算符。例如:

            -123    +500    !b       a++

② 二元运算符。二元运算符要求有左、右操作数,表达式形式为:

            左操作数  Op  右操作数

+,-,*,/,>,<等都是二元运算符。例如:

            i+1      a*3      x>y

③ 三元运算符。C++语言只有一个三元运算符,就是条件运算符。表达式形式为:

          操作数 1  ? 操作数 2  : 操作数 3

例如: a ? b : c

一个复杂表达式会包含多个运算符。运算符之间的运算次序由各运算符的优先级(优先关系)和结合性决定。

表达式中的运算符按优先级从高到低运算,带括号的内层优先,同级运算符从左到右运算,见表1.3。这些规则与习惯的数学规则一致。

表1.3 常用运算符的功能、优先级和结合性

从左至右结合的运算符,首先计算左操作数,然后计算右操作数,最后按操作符求值;从右至左结合的运算符,则首先计算右操作数,然后计算左操作数,最后按操作符求值。

例如: (a+b) * (x-y)

其中,“*”运算符从左至右结合,首先计算左操作数(a+b),然后求右操作数(x-y),最后作*运算。

又如: - (x+y)

其中,一元“-”从右至左结合,首先计算右操作数(x+y),然后对结果求负。

本章讨论算数运算和逻辑运算。位运算将在第5章中讨论。

1.5.2 算术表达式

1.基本运算

算术表达式由算术运算符和操作数组成,结果值是算术值。基本算术运算符有:

          +  加法,或一元求正             /      除法
          -  减法,或一元求负             %     求模(求余)
          *  乘法                       sizeof   求存储字节

“求模”运算是计算两个整数相除的余数。例如:

            7%4                     //等于3
            5 % 21                  //等于5
            12 % 2.5                //错误,操作数不能为浮点数

sizeof求数据类型占内存的字节数。例如:

            int a;
            sizeof(a)               //等于4,在16位机上等于2
            sizeof(int)             //等于4
            sizeof(26756)           //等于4
            sizeof(double)          //等于8
            sizeof(0.25)            //等于8

2.运算符的多义性

作用于基本数据类型的算术运算符意义很明确,但要注意,一些符号意义与上下文有关。

例如,“*”号在以下不同语句中有不同的意义:

            int a = 35;
            int*p=&a;            //指针类型说明符
            a=a*4;               //算术乘
            *p=5**p;             //第1,3个“*”是间址访问,取内容;第2个“*”是算术乘

以上注释中说明了各个“*”号的意义。表达式:

            5 * *p

两个“*”中间的空格可以不写。系统可以正确运行,这是因为间址访问符是右结合运算符,而且优先级高于算术乘,编译器能够识别。程序员为了明确起见,可以写为:

            *p = 5 * (*p )

又如: -5.6+3.2

不能理解为: -(5.6+3.2)

因为一元负运算符优先级高于加运算符,而且为右结合,所以解释为:

            (-5.6)+3.2

3.自增和自减

在程序中,我们经常会用到以下操作:

            i = i+1和i = i-1

这两个操作分别称为变量的自增和自减。C++用“++”和“--”运算符描述这两种常用运算,见表1.4。

表1.4 自增和自减

后置式和前置式在独立使用的时候没有区别。但当它作为子表达式时,会对其他变量产生不同的影响。例如:

            int a = 0, b = 0, i = 0;
            a=++i;      //a为1,i为1
            b=i++;      //b为1,i为2

执行第2行语句时,++i是前置式的,先自增,然后把i的值赋给 a。

而执行第3行语句时,i++是后置式的,先读出i的值赋给b,然后自增。虽然“++”的优先级高于“=”,但这里语义起作用。

又如: c = a++ + ++b;

与以下几种书写方式等价:

① c = (a++) + (++b);

② b = b+1; c = a+b; a = a+1;

③ b ++; c = a+b; a++;

④ ++b; c = a+b; ++a;

再如: c = ++a + ++b;

与以下几种书写方式等价:

① c = (++a) + (++b);

② b = b+1; a = a+1; c = a+b;

③ b++; a++; c = a+b;

④ ++b; ++a; c = a+b;

显然,自增运算符的连用可读性较差,没有相当的熟练程度最好不用。

4.类型转换

表达式是表达一个值的式子,算术表达式的值的类型由操作数的类型决定。

① 如果运算符左右操作数类型相同,则运算结果也是相同类型。例如:

            6+5         //结果为整型值11
            2/4         //结果为整型值0。因为左右操作数都是整数,做整除

② 如果运算符左右操作数类型不同,则首先把类型较低(存储要求、示数能力较低)的数据转换成类型较高的数据,然后运算。例如:

            cout << 3 + 'A' << endl;

把1字节长的char类型字符'A'转换成1个字长的int类型65,输出:

            68

又如:

            2.0/4        //结果为浮点型值0.5

③ 赋值的类型转换。当把一个表达式的值赋给一个变量时,系统首先强制把运算值转换成变量的类型,然后执行写操作。这种强制类型转换是易于理解的,因为被赋值的对象类型已经定义,必须把类型不一致的右操作数转换后才能写入指定存储单元。

【例1-10】类型转换测试。

            #include<iostream>
            using namespace std;
            int main()
            {  int a;
              char c;
              double x;
              a=2.0/4;                   //把0.5赋给a
              x=2.0/4;                   //把0.5赋给x
              cout << a << '\t' << x << endl;
              a=3+'A';                  //把68赋给a
              c=3+'A';                  //把68转换为字符'D',赋给c
              cout << a << '\t' << c << endl;
              cout<<3+'A'<<endl;        //表达式值为整型
            }

程序运行结果:

            0   0.5
            68  D
            68

④ 强制类型转换。C++可以用类型符对表达式值转换成指定类型,一般形式为:

(类型) (表达式)

或 (类型) 表达式

或 类型 (表达式)

例如:

            (int)(x+y)     //把x+y的结果转换成整型
            (char)70       //把整数70转换成字符 'F'
            double(a)+y    //把a的值转换成double类型再加上y的值

注意: (double)(2/4)

把2/4的运算结果转换成double,等于0。而

            (double)2/4

先把2强制转换为double型,然后按照运算类型转换的原则,自动把4转换为double型,最后相除的结果等于0.5。

赋值时的类型转换和用类型符实现的类型转换是强制性的,所以,要把低类型数据转换成高类型时,一般不会发生什么问题。反之,把高类型数据转换成低类型,就有可能引起数据错误或丢失。这是程序员应该特别注意的。

1.5.3 逻辑表达式

逻辑表达式用于判断运算,结果值只有两个:判断成立,则为逻辑“真”(true);否则为“假”(false)。C++用true或1值表示计算结果为逻辑真,用false或0值表示逻辑假。而在程序运行中,即表达式求值过程中,非0值都作为逻辑真。

构成逻辑表达式的运算符有关系运算符和逻辑运算符。

1.关系运算

关系运算即比较运算,用于算术值比较。C++的关系运算符有:

            <  小于          <= 小于等于      >  大于      >= 大于等于
            == 等于          != 不等于

前4种比较运算符的优先级高于“==”和“!=”。

例如,设a=1,b=2,c=3,则

            a<=b             //逻辑真,值为true(1)
            (c>b)==a         //逻辑真,值为true(1)
            b!=1/c           //逻辑真,值为true(1)
            a+b>c            //逻辑假,值为false(0)

注意,如果想用表达式

            c > b > a

表达“c的值大于b的值,b的值大于a的值”,将得到错误的判断结果。按照运算符左结合的原则,首先计算 c>b,值为 true(1),然后用整型值 1 计算1>a,结果值为 false(0),失去逻辑判断意义。导致错误的原因是,C++表达式在运算过程中用 0 或 1 表示逻辑值。C++编译器会对这种表达式提示警告(warning)。正确的方法是用逻辑表达式:

            c > b && b > a

字符值用ASCII码进行比较:

            'A'>'B'          //值为false(0)
            50<'D'           //值为true(1)

2.逻辑运算

逻辑运算用于判断多种逻辑情况在某些条件组合之下的结果。C++的逻辑运算符有:

            && 逻辑与       || 逻辑或     ! 逻辑非

“&&”和“||”是二元运算符,“!”是一元运算符。

“逻辑与”只有在左、右操作数都为true(非0)时,结果才为true。

“逻辑或”只要左、右操作数中有一个为true(非0),结果就为true。

“逻辑非”表示取操作数逻辑相反值。

表1.5为基本逻辑运算的真值表,其中列出了左操作数a和右操作数b取不同组合时,各种逻辑运算的结果。

表1.5 逻辑真值表

“!”运算符优先级高于“&&”,“&&”优先级高于“||”。在一个表达式中,可能包含算术运算、关系运算和逻辑运算,优先级从高到低为:

            ! → 算术运算符 → 关系运算符 → && → ||

例如:

            a>b&&b>c       相当于    (a>b)&&(b>c)
            a==b||c==d     相当于    (a==b)||(c==d)
            !a||a>b        相当于    (!a)||(a>b)

算术值和字符值也可以参与逻辑运算:

            'x'&&'y'         值为1
            5>3||2&&1-!0     值为1

【例1-11】写出描述点A(x,y)落在图1.10中灰色部分(不压线)的C++表达式:

图1.10 用关系表达式描述落点

          -2<x && x<2 && -2<y && y<2 && x*x+y*y>1

请读者分析这个表达式的运算次序。

1.5.4 赋值表达式

前面,我们已经使用过赋值表达式了。赋值表达式的作用是把数据值写入变量。C++赋值表达式的一般形式为:

            变量 = 表达式

由于C++使用“赋值表达式”的概念,赋值号右边“表达式”也可以是赋值表达式,使得赋值操作可以拓展。

例如: a = b = 10

相当于: a = ( b = 10 )

表达式b=10的值为10,b的值等于10,把10赋给a。也可以用赋值号右结合来理解,首先把10赋给b,然后把b的值赋给a。但是,

            ( a = b ) = 10

就不一样了。括号改变了执行顺序,首先应执行a=b,把b的值写入a,表达式的值确定于a,然后执行a=10。上式对a做了两次写操作,对b做了一次读操作。

【例1-12】赋值表达式测试。

            #include <iostream>
            using namespace std;
            int main()
            {  int a,b;
              a = b = 10;
              cout << a << '\t' << b << endl;
              ( a = b ) = 5;
              cout << a << '\t' << b << endl;
            }

程序运行结果:

            10     10
            5      10

赋值表达式又称为“左值表达式”,因为表达式的值确定于一个存储单元,所以可以放在赋值号的左边,也可以放在赋值号的右边。而

            3 + 7

这样的表达式,虽然能够表达整型值10,但不能确定于一个存储单元,所以只能放在赋值号的右边,称为“右值表达式”。

例如:

            a=b=3+7            //正确
            3+7=a              //错误
            a=b+3=10           //错误
            (a=b+3)=10         //正确

C++还有一批用于简化代码的复合赋值运算符:+=、-=、*=、/=、%= 等。一般形式为:

            A Op=B     等价于       A=A Op B

            a+=b       等价于       a=a+b
            a-=b       等价于       a=a-b
            a*=b       等价于       a=a*b
            a/=b       等价于       a=a/b
            a %=b      等价于       a=a%b

例如:

            a+=10       等价于       a=a+10
            b*=2+3  +优先级高于*=,等价于 b=b*(2+3)

1.5.5 条件表达式

条件表达式由条件运算符和操作数组成,根据逻辑值决定表达式的求值。

条件表达式形式为:

          操作数 1 ? 操作数 2  : 操作数 3

执行过程是:首先对“操作数 1”求值,其值非 0 时,表达式的值为“操作数 2”的值;否则,表达式的值为“操作数3”的值。

“操作数1”通常是判断的条件表达式或逻辑表达式。例如:

            a > b ? a : b

表达式的功能是取a、b中的大值。要把这个值赋给变量max,可以用以下语句表示。

            max = a > b ? a : b;

“操作数1”也可以是其他类型的表达式,因为C++把算术值也视为逻辑值。例如:

            4+2 ? 0 : 1

是一个合法的表达式。当然,这个式子没有什么意义。

如果“操作数 1”包含程序运行时动态变化的数据,那么,表达式会起到简单的控制作用。例如:

            x>y?x-y:y-x    //求|x-y|

条件运算符按右结合方式匹配。例如,求a、b、c中的最大值,用条件表达式可以表示为:

            a>b ? a>c ? a : c : b>c ? b : c

相当于:a>b ? (a>c ? a : c) : (b>c ? b : c)

【例1-13】求3个整数中的最大值。

            #include <iostream>
            using namespace std;
            int main()
            {  int a,b,c,max;
              cin>>a>>b>>c;                    //输入数据
              max=a>b?a>c?a:c:b>c?b:c;         //求最大值
              cout << "max = " << max << endl;
            }

1.5.6 逗号表达式

用逗号连接起来的若干个表达式称为逗号表达式。一般表示形式为:

            表达式 1 ,表达式 2 ,…,表达式 n

逗号表达式有两层含义:第一,各表达式按顺序执行;第二,逗号表达式也表达一个值,这个值是最后一个表达式的值。

例如,逗号表达式:

            3*5, a+b, x=10

由3个互不相干的表达式组成,仅仅是顺序执行而已。如果有:

            x = ( a = 3, 2 * 6 )

把逗号表达式a=3, 2*6的值赋给x,则x的值为12。但如果表达式写为:

            x = a = 3, 5 * 6

因为逗号的运算级别最低,所以它是由两个表达式构成的逗号表达式,则x的值为3。