C++从入门到精通(第2版)
上QQ阅读APP看书,第一时间看更新

第4章C++中的公式—运算符和表达式

本章视频教学录像:43分钟

在数学中,我们通常要对数据进行加减乘除等运算后才能得到想要的结果。同样,在C++的编程世界中,运算符和表达式就像是数学运算中的公式一样,是必需的。要想正确、灵活地使用运算符和表达式,需要编程开发者有扎实的基本功。认真、深入地学习本章,将助我们在编程之路上前行,迈步更加坚实。

本章要点(已掌握的在方框中打钩)

□ 运算符

□ 表达式

□ 运算符的优先级

□ 程序实例

我们可以使用方程和公式解决数学中的问题,也可以使用表达式解决编程中的问题。本章将介绍C++各种运算符的使用方法以及由运算符组成的表达式。

4.1 C++中的运算符和表达式

本节视频教学录像: 4分钟

和其他高级语言一样,C++语言也是由多种运算符组成了各种各样的运算,比如“+”、“-”、“*”和“/”分别表示四则运算的加、减、乘、除运算。如果表达式中有两个或两个以上不同的运算符,则按一定的次序来计算,这种次序称为优先级。由运算符、操作数和括号按照一定的规则组成的式子叫作表达式。

4.1.1 运算符

在C++语言中包含有多种运算符,不同的运算符有不同的运算次序,比如 “*”、“/”的优先级高于“+”、“-”的优先级。如果表达式中相同的运算符有一个以上, 则可从左至右或从右至左地计算,这称为结合性。“+”、“-”、“*”和“/”的结合性都是从左至右的。

下表所示为C++语言中各种运算符的优先级、功能说明以及结合性。

4.1.2 表达式

在了解了运算符的基本知识后,下面简单认识一下什么是表达式,它具有什么特点。

⑴ 表达式是由运算符、操作数(常量、变量、函数等)和括号按照一定的规则组成的式子。

⑵ 可以将常量、变量和函数认为是最简单的表达式。

⑶ 表达式可以嵌套。

⑷ 每个表达式都有一个值。

⑸ 在计算时要考虑运算符的优先级、结合性及数据类型的转换。

⑹ 计算机中的表达式都要写在一行中。

⑺ 表达式有算术、赋值、关系、逻辑、条件和逗号等。

⑻ 在表达式的后边加个分号就是表达式语句。除了控制语句外,几乎都是表达式语句。

提示

这么多知识我可怎么记得住啊!更不知道该怎么使用了!

没关系,当读完后续章节,你会觉得运算符和表达式原来是这么简单啊!

4.2 算术运算符和表达式

本节视频教学录像: 10分钟

在C++中的算术运算和数学中的四则算术运算一样,不同的只是前者用程序语言来描述。

4.2.1 基本算术运算符

基本的算术运算有加法、减法、乘法、除法和取模(求余数)。下表是基本算术运算符说明。

4.2.2 算术运算符和算术表达式

有算术运算符的表达式叫算术运算表达式。下面通过举例的形式,详细说明基本算术运算符和表达式的用法。

1. 加、减、乘运算

      int a,b,c;
      a=10;
      b=5;
      c=a+b*3-1;

输出c的结果是24。因为“*”的优先级高于“+”和“-”,并且结合性为右结合,所以先算b*3,然后算a加上b与3的乘积15,最后算减法-1,得到结果c=24。

2. 取模运算

      21%6       //结果是3
      4%2        //结果是0
      4.0%2      //程序报错,%运算符要求左右必须为整数

取模运算“%”要求运算符的两边必须都是整数,如果任何一边不是整数,程序就会报错。

3. 整除运算

      5/4        //结果是1
      4/5        //结果是0

当“/”运算符用于两个整数相除时,如果商含有小数部分,将被截掉,不进行四舍五入。

4. 浮点除运算

      5/4.0       //结果是1.25
      4.0/5       //结果是0.8

要进行通常意义的除运算,则至少应保证除数或被除数中有一个是浮点数或双精度数,可以在参加运算的整数值后面补上小数点与0 作为双精度(double)常量参加运算。

注意

在使用算术运算符时,需要注意有关算术表达式求值溢出时的相关问题的处理。在做除法运算时,若除数为零或实数的运算结果溢出,系统会认为是一个严重的错误而中止程序的运行。而整数运算产生溢出时则不认为是一个错误,但这时运算结果已不正确了,所以对整数溢出的处理是程序设计者的责任。

使用算术运算符需要注意以下4个问题。

⑴“/”运算符的两个运算对象均为整数时,其结果是整数;如果有一个是浮点型数据,其结果则是浮点数。

⑵ 取模运算符“%”要求参与运算的两个数均为整数。

⑶ 遵循算术的自然特征,例如禁止除数为0。

⑷ 防止数据长度的溢出。

4.2.3 自加和自减运算符

++(自加)、- -(自减)是C++中使用方便且效率很高的两个运算符,它们都是单目运算符,运算顺序从右至左。这两个运算符有前置和后置两种形式,所谓前置是指运算符在操作数的前面,后置是指运算符在操作数的后面。

下面通过几个例子,详细说明自加、自减算术运算符和表达式的用法。

1. 自加和自减单独运算

      i++;                //++后置
      --j;                //--前置

无论是前置还是后置,这两个运算符的作用都是使操作数的值增1 或减1,但对由操作数和运算符组成的表达式的值的影响却完全不同。

2. 自加前置运算后直接赋值

      int i=5;
      x=++i;            //i先加1(增值)后再赋给x
      y=i; //i=6,x=6,y=6

这是自加运算符的前置形式。通过运算最终i、x、y的值都等于6。

3. 自加前置运算后再赋值

      int i=5;
      ++i;               //i自加1,值为6
      x=y=i;             //i=6,y=6,x=6

这是自加运算符的前置形式。该题目与上题没有太大的不同,只是把上题中的x=++i语句拆分成了两句++i和x=i单独实现,结果与上题一样。

4. 自加后置运算后直接赋值

      int i=5;
      x=i++;            //i赋给x后再加1
      y=i; //x=5,i=6,y=6

这是自加运算符的后置形式。通过运算最终x=5,而i和y的值等于6。

5. 自加后置运算后再赋值

      int i=5;
      i++;
      x=y=i;             //i=6,y=6,x=6

这是自加运算符的后置形式,该题目也是把“x=i++”语句拆分成了两句,分别是“i++”和“x=i”,但是运算结果是i、x、y的值都等于6,从中大家可以体会前后置运算的异同。

比较上例结果可知,若对某变量自加(自减)而不赋值,结果都是该变量本身自加1或减1;若某变量自加(自减)的同时还要参与其他的运算,则前置运算是先变化后运算,后置运算是先运算后变化。

技巧

由于++、- -运算符内含了赋值运算,所以运算对象只能赋值,不能作用于常量和表达式,比如5++、(x+y)++都是不合法的。

【范例4-1】 计算自加和自减表达式的值。

⑴ 在Visual C++ 6.0中,新建名为“计算自加自减表达式的值”的【C++ Source File】源程序。

⑵ 在代码编辑窗口中输入以下代码(代码4-1.txt)。

      01  #incIude<iostream>     //包含标准输入/输出头文件
      02  using namespace std;
      03  void main()
      04  {
      05         int a=2;                //初始化a
      06         cout<<a++<<endI;       //输出2,++后置运算,先输出2后,a再加1,值改变为3
      07         cout<<a--<<endI;       //输出3,--后置运算
      08         //输出3,++前置运算,先进行a的加1运算,值改变为3,再输出3
      09         cout<<++a<<endI;
      10         cout<<--a<<endI;       //输出2,--前置运算
      11         //输出-2,++后置运算,-负号和++运算级别相同,都是右结合的,
      12         //所以先计算++后11置后,返回结果3取负后输出,之后a的值改变为4。
      13         //这里负号只影响输出,对a没有影响
      14         cout<<-a++<<endI;
      15         cout<<-a--<<endI;      //输出-3,--后置运算
      16  }

【运行结果】

编译、连接、运行程序,即可在命令行中输出如图所示的结果。

【范例分析】

仔细体会这道题目中关于自加自减前置和后置运算,对你以后灵活使用它们有很大的帮助。分析结果,我们可以得出这样的结论:自加自减运算,如果是单独的语句,没有区别;如果是其他语句的组成部分,则前置运算是先计算后赋值,后置运算是先赋值后运算。

4.3 逻辑运算符和表达式

本节视频教学录像: 5分钟

公司开会,需要对某个决议进行表决,只有全票通过,决议才能通过,只要有一票不通过,决议就通不过,如果用逻辑关系来描述,这叫“与”;还是这个公司进行表决,只要有一票反对,表决结果就是反对,只有大家都不反对,表决结果才是不反对,如果用逻辑关系来描述,这叫“或”;其中某一票由赞同变为反对,或者由反对变为赞同,这叫“非”。

4.3.1 逻辑运算符

逻辑运算符用于实现逻辑运算和逻辑的判断,返回类型是布尔(bool)型。下表为逻辑运算符。

4.3.2 逻辑表达式

在实际应用逻辑表示式之前,我们需要明确逻辑运算表达式有哪些,结果是怎么样的。下面表列出了逻辑运算关系。

逻辑运算符的操作数为bool型,当为其他数据类型时,则将它转换成bool值参加运算。下面举例说明逻辑运算符和表达式的用法。

假设a=10、b=5、c= -3,分析下面表达式的结果。

!a值为faIse

非0数求非运算,结果为false;相反为0的数求非运算,结果为true。

a && b值为true

&&两边都是非0数值,结果为true。

a || b值为true

||两边只要有一边数值不为0,结果就为true。

a+c >= b && b值为true

因为“+”的优先级高于“>=”,先计算a+c得7,再与b比较,7 大于等于5 成立,结果为true,转换为数值类型1,最后再做逻辑与运算,1和b逻辑与结果得true。

注意

C++对于二元运算符“&&”和“||”可进行短路运算。由于“&&”与“||”表达式按照从左到右的顺序进行计算,如果根据左边的计算结果能得到整个逻辑表达式的结果,右边的计算就不需要进行了,该规则叫短路运算。

      (num!=0)&&(1/num>0.5)

当num为0 时,&&操作符的第1个操作数结果为false,这样就不会计算第2个操作数,同时避免了计算1/num,当num为0 时的错误。

技巧

当表示的逻辑关系比较复杂时,用小括号将操作数括起来是一种比较好的方法。

4.4 关系运算符和表达式

本节视频教学录像: 5分钟

关系运算也叫比较运算,用来比较两个表达式的大小关系。

4.4.1 关系运算符

在解决许多问题时都需要进行情况判断,C++中提供有关系运算符用于比较运算符两边的值。比较后返回的结果为布尔常量true或false。下表为关系运算符。

4.4.2 关系表达式

关系表达式就是由关系运算符将两个操作数连接起来的式子。这两个操作数可以为常量、变量、算术表达式以及后面讲到的逻辑表达式、赋值达式和字符表达式等。

下面举例说明关系运算符和表达式的用法。

1. 整数和整数的关系表达式

      a=1;
      b=2;
      c=3;
      d=4;
      a+b>c+d;

“+”的优先级高于“>”,所以先分别求出a+b和c+d的值,然后进行关系比较,运算结果为false。

2. 字符和字符的关系表达式

      'a'<'b' + 'c';

“<”右边需要求算术运算和,所以字符'b'和'c'分别由字符型隐式地转换为整型数98和99,求和结果为197。“<”左边的字符型也需要转换为整型数96才能进行比较,整个表达式的值为true。

3. 关系表达式连用

      a>b>=c>d;

关系运算符优先级相同,所以按照从左至右依次计算。假设a=1、b=2、c=0、d=4,先计算a>b的值为false,然后计算false>=c,因为“>=”两边的数据类型不一致,布尔类型false转换为整型数0,0>=0比较结果为true,最后计算true>3,true转换为数值型数1,1>4比较结果为false,所以整个表达式的结果为false。

注意

关系运算符的比较运算由两个等号组成,不要误写为赋值运算符=。

若关系运算符的计算结果继续用在表达式中,则true与false分别当成1 与0。关系运算符的操作数可以是任何基本数据类型的数据,但由于实数(float)在计算机中只能近似地表示某一个数,所以一般不能直接进行比较。当需要对两个实数进行==、!=比较时,通常的做法是指定一个极小的精度值,若两个实数的差在这个精度之内,就认为两个实数相等,否则为不等。

对下面两个表达式进行分析。

1. 等于

      x==y
      应写成
      fabs(x-y)<1e-6

2. 不等于

      x!=y
      应写成
      fabs(x-y)>1e-6

fabs(x)的作用是求double类型数x的绝对值,使用时需要头文件 <math.h>。fabs(x-y)<1e-6表示x和y的差的绝对值小于0.000001,说明x和y的差值已经非常小,可以认为二者相同。

4.5 条件运算符和表达式

本节视频教学录像: 2分钟

C++中唯一的一个三目运算符是条件运算符。它能够实现简单的选择功能,类似于条件语句,故称为条件运算符。条件运算符优先级比较低。下表为条件运算符和表达式。

其中,A、B和C分别是3 个表达式。该运算符的功能说明如下。

⑴ 计算A。

⑵ 如果A的值为true(非0),返回B的值作为整个条件运算表达式的值。

⑶ 如果A的值为false(0),返回C的值作为整个条件运算表达式的值。

⑷ 条件运算表达式的返回类型将是B和C这两个表达式中数据类型高的那种类型。

简单条件表达式:

      a=(x>y ? 12 : 10.0);

若x>y(值为true),将12赋给a,否则a=10.0,但a的类型最后都是double。

4.6 赋值运算符和表达式

本节视频教学录像: 6分钟

在前面的程序当中,我们已经接触过赋值运算了。本节将介绍赋值运算符和表达式。

4.6.1 赋值运算符

赋值运算符之所以称为赋值运算符,是因为其功能是赋给变量值,除了在定义变量时给变量赋初值外,还用于改变变量的值。各赋值运算符如下表所示。

C++提供的赋值运算符功能是将右表达式(右操作数)的值放到左表达式表示的内存单元中,因此左表达式一般是变量或表示某个地址的表达式,称为左值。左值在运算中作为地址使用。右表达式在赋值运算中是取其值使用,称为右值。所有的赋值运算左表达式都要求是左值。

4.6.2 赋值表达式

一般可把赋值语句分为简单赋值语句和复合赋值语句两种。

1. 简单赋值语句

      int i=100;         //变量名为i的地址中内存数据是100
      char a='A',b,c;    //声明3个字符型变量,同时变量a赋值为字符“A”
      //变量b的值为'A'+1,即65,但是b是字符型,66再转换为字符型数据“B”
      //变量c的值等于变量b的值'B'
      c = b = a + 1;

如果a的地址是2000,此时该地址中存放的数据是“A”;则b的地址是2001,此时该地址中存放的数据是“B”;则c的地址是2002,此时该地址中存放的数据也是“B”。

2. 复合赋值语句

      *=
      等价于
      x=x * y

对赋值运算还有下列几点说明。

(1) 复合赋值运算符所表示的表达式不仅比一般赋值运算符表示的表达式简练,而且所生成的目标代码也较少,因此在C++语言程序中应尽量采用复合赋值运算符的形式表示。

(2) 在C++中还可以连续赋值,赋值运算符具有右结合性,例如x=y=2.6;(赋值运算符是从右至左计算的,所以表示式相当于x=(y=2.6),根据优先级,先计算括号里面的赋值语句,再把y的值赋给x)。再例如a=b=3+8,按照右结合,该表达式先计算3+8,然后将11赋给b,再将b的值11赋给a。

【范例4-2】 赋值运算。

⑴ 在Visual C++ 6.0中,新建名为“赋值运算”的【C++ Source File】源程序。

⑵ 在代码编辑窗口中输入以下代码(代码4-2.txt)。

      01  #incIude<iostream>
      02  using namespace std;
      03  void main()
      04  {
      05    int a=123,b=3,c=2,d=456,x=2;
      06    c+=a;       //等价c=c+a
      07    d%=b;       //等价d=d%b
      08    x+=x-=x*x;
      09    cout<<”c=”<<c<<”d=”<<d<<”x=”<<x<<endI;
      10  }

【运行结果】

编译、连接、运行程序,即可在命令行中输出如图所示的结果。

【范例分析】

x+=x-=x*x可分解成以下几步进行:第1步x+=x-=4;第2步x+=x=x-4,等价于x=-2 、x+=x,这时x的值发生了改变,由2变为-2;第3步x=x+x,x的最后计算结果为-4。

4.7 逗号运算符和表达式

本节视频教学录像: 3分钟

逗号可作分隔符使用,将若干个变量隔开,如“int a,b,c;”。逗号亦可作运算符使用,称为逗号运算符,用于将若干个独立的表达式隔开。

逗号运算符使用的一般形式如下所示。

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

使用逗号运算符可以将多个表达式组成为一个表达式,逗号表达式的求解过程为先求表达式1的值,再求表达式2的值……最后求表达式n的值。整个逗号表达式的结果是最后一个表达式n的值。它的类型也是最后一个表达式的类型。

在C++程序中,逗号运算符常用来将多个赋值表达式连成一个逗号表达式。

1. 逗号表达式单独运算

假设a=3,b=5,c=7

求表达式a=a+b, b=b*c, c=c-a;

表达式依次计算出a的值为8,b的值为35,c的值为-1。

2. 逗号表达式赋值运算

      x=(a=a+b,b=b*c,c=c-a);    //该表达式的结果等于-1,即x的值为-1

逗号运算符还用在只允许出现一个表达式而又需要多个表达式才能完成运算的地方,用它将几个表达式连起来组成一个逗号表达式。

在C++语言所有的运算符中,逗号表达式的优先级最低。

提示

在学习中需要区分运算符、表达式和语句的不同。不同类型的操作数赋值时,应尽量进行显式转换,隐式转换容易犯错误。在优先级和结合性上也容易犯错误。通常在表达式中加上圆括号,这样既能够增强程序的安全性,又可以提高程序的可读性。

4.8 运算符的优先级

本节视频教学录像: 3分钟

C++的大多数运算符有不同的优先级,各类运算符还有不同的结合性,可以总结出如下的规律。

(1) 运算符的优先级按单目、双目、三目、赋值依次降低。单目运算是从右至左的,旨在与右边的数结合在一起形成一个整体,因此优先级高。运算中的+(正)、-(负)、++、- -,逻辑运算中的取反!,按位运算中的取反~,从各类运算中提取到单目运算中。赋值运算之所以优先级低且为右结合,是因为右边的表达式计算完后才赋值给左边的变量。

(2) 算术、位移、关系、位、逻辑运算的优先级依次降低。

【范例4-3】 运算符优先级。

⑴ 在Visual C++ 6.0中,新建名为“运算符优先级”的【C++ Source File】源程序。

⑵ 在代码编辑窗口中输入以下代码(代码4-3.txt)。

      01  #incIude<iostream>
      02  using namespace std;
      03  void main()
      04  {
      05         int a=1,b=2,m=3,n=4,x,y;
      06         x=-m++;                      //自加和符号优先级相同,但运算顺序是从右至左,
                                          故先算自加再取负,最后赋值
      07         x=x+8/++n;                   //先自加,再求除,然后求和,最后赋值
      08         y=(n=b>a)||(m=a<b);          //先求括号,再去或运算,最后赋值
      09         cout<<"m="<<m<<endI;
      10         cout<<"n="<<n<<endI;
      11         cout<<"x="<<x<<endI;
      12         cout<<"y="<<y<<endI;
      13  }

【运行结果】

编译、连接、运行程序,即可在命令行中输出如图所示的结果。

【范例分析】

在表达式“x=-m++”中,先算自加运算,又因为m是自加后置,所以经过运算x=-1、m=4。

对于表达式“x=x+8/++n”,先计算自加运算,因为n是自加前置,所以该式等价于先计算“n=n+1”,即n=5,再计算“x=x+8/n”,经过运算“x=1+8/5”,按照前面讲述的运算方法,x=2,是整数。

对于表达式“y=(n = b > a) || (m = a < b)”,先计算左括号中的“n = b > a”,因为关系表达式的优先级高于赋值表达式,经过计算n=1;因此或运算是短路运算符,当运算符左边已经为true时,右侧就不再计算了,也就是说,“m=a<b”将不会计算,所以m的值不会改变为1,而是保持为4。

4.9 综合应用

本节视频教学录像: 5分钟

本节通过两个数学计算的程序,来学习C++中运算符和表达式的综合应用。

【范例4-4】 输入三角形的3边长,求三角形面积。

假设三角形的3条边分别是a、b、c,已知面积公式如下。

area=

s=(a+b+c)×0.5

需要注意的是,开平方用到了数学函数库“math.h”提供的sqrt函数。

⑴ 在Visual C++ 6.0中,新建名为“求三角形面积”的【C++ Source File】源程序。

⑵ 在代码编辑窗口中输入以下代码(代码4-4.txt)。

      01  #incIude<iostream>
      02  #incIude<cmath>               //包含数学函数头文件,因为程序当中用到了开方函数sqrt
      03  using namespace std;
      04  void main()
      05  {
      06    float a,b,c,s,area;
      07    cin>>a>>b>>c;
      08    s=(a+b+c)*1/2.0;            //使用公式计算面积值
      09    area=sqrt(s*(s-a)*(s-b)*(s-c));
      10    cout<<”a=”<<a<<”b=”<<b<<”c=”<<a<<”s=”<<s<<endI; //依次输出各个变量的值
      11    cout<<”area=”<<area<<endI;
      12  }

程序流程图如下。

【运行结果】

编译、连接、运行程序,在命令行中依次输入三角形的3条边,按【Enter】键即可输出三角形的面积,如图所示。

【范例分析】

一个完整的程序包含输入、运算和输出3部分。该范例首先使用cin标准输入函数分别接收a、b和c,作为三角形的3条边长,然后调用数学函数库的开方函数,计算出三角形的周长和面积,最后使用cout输出结果。

因为程序中包含sqrt函数,所以头文件中必须包含math。

如果输入的三条边不能组成一个三角形将会出现什么样的结果呢?我们将在下一章节学习对能否组成三角形的判断。

4.10 实战练习

在Visual C++ 6.0中新建【C++ Source File】源程序,要求实现以下功能。

(1) 分别输入两个半径值。

(2) 比较两个值的大小关系。

(3) 根据输入的两个半径值计算圆环的面积。