第4章 将语句编织成程序
学过C++中的各种数据类型, 就知道如何使用各种数据类型定义变量来描述现实世界中的各种事物了。现在,我们可以将一个工资统计程序大致写成下面这个样子:
// 工资统计程序 int main() { // 表示员工个数的常量NUM const int NUM = 100000; // 保存所有工资的数组 int arrSalary[NUM]; // 保存平均工资的变量 float fSalaryAver = 0.0; // 对工资进行处理… return 0; }
但是,我们现在只是知道如何用数据类型定义变量来表示现实世界中的数据,而对于如何处理这些数据以解决问题还一无所知。我们不知道如何方便地输入这100000个工资数据,更不知道如何计算这100000个工资数据的平均工资。程序的两个任务——描述数据和处理数据。现在我们已经完成了第一个任务,用各种类型的变量描述现实世界中的数据。那么接下来,我们就看看在C++中如何完成第二个任务——处理数据,从而获得结果数据最终解决问题。
4.1 用运算符对数据进行运算
对数据的处理最常见的就是对数据进行运算,以获得某个运算结果。就像在现实世界中,我们对1和2 进行加法运算,可以得到运算结果3一样,在C++中,我们同样可以用两个int类型的变量a和b来表示1和2,那么,又如何对a和b进行加法运算得到结果数据3呢?
4.1.1 用表达式表达设计意图
在计算数学题的时候,如果想知道两个数的和,总是先用加法运算符号“+”连接两个加数列出加法算式,然后再计算整个算式得到最终的和值。例如:
1 + 2 = 3
在C++中也是如此,如果想对数据进行处理获得运算结果,就要用一个式子将数据的运算处理过程描述出来,因为这个式子表达了我们对这些数据的处理意图,所以这个式子也被称为表达式。而程序在执行计算一个表达式的时候,会按照这个表达式所描述的运算过程对数据进行运算,最终获得整个表达式的运算结果。
在C++中,一个表达式由操作符、操作数和标点符号(必须是英文的)三部分组成,其作用是描述一个对数据的运算过程。表达式的核心是操作符和操作数。操作数就是要参与运算的数据,它可以是变量表示的数据,也可以是直接表示的常数。而连接这些操作数表达运算意图的各种符号就是操作符了,比如表示加法运算意图的“+”,表示乘法运算意图的“*”等。操作数是操作符的处理对象,而操作符则表达了对所连接的操作数的处理方式。比如,一个加法运算表达式“a + 5”中,变量“a”和常数“5”是这个表达式的操作数,而连接这两个操作数的是加法运算操作符“+”,表示将它所连接的两个操作数“a”和“5”进行加和运算得到结果。如果这个表达式是某个更复杂表达式的一部分,那么这个结果将作为这个表达式的值,继续参与运算,直到最终得出整个表达式的值。例如:
// 定义需要用到的变量 int a,b,c; // a、5是操作数,=是操作符,它描述的是一个赋值运算:将常数5赋值给变量a a = 5; // b、a、5是操作数,=、+都是操作符 // 它描述的计算过程是:先计算变量a和常数5的和,然后将其赋值给变量b b = a + 5; // a、b、c是操作数,=、*、-是操作符,()是标点符号 // 它描述的计算过程是:首先计算变量b和变量a的差,然后将其与变量a相乘计算积, // 最后将积赋值给变量c。运算过程如图4-1所示。 c = a * (b - a);
图4-1 表达式的计算过程
表达式中的操作符决定了整个表达式中操作数的个数。大多数操作符需要两个操作数,比如加法操作符“+”,就需要一个加数和一个被加数。这种需要两个操作数的操作符称为二元操作符,如常见的加、减、乘、除操作符。另外一些操作符需要一个或者三个操作数,相应地被分别称为一元操作符和三元操作符。C++提供了丰富的操作符,用于表达数据之间复杂的运算关系,如有表示加、减、乘、除的算术操作符,也有表示大小关系的关系操作符等。下面就来分别了解一下如何运用这些操作符以实现对数据的处理。
4.1.2 算术操作符
在开发实践中,用得最多的就是用于数学计算的算术操作符了。C++提供的算术操作符有以下几种。
l +(加):计算两个数的和。
l “-”(减):计算两个数的差。
l “*”:(乘):计算两个数的积。
l /(除):计算两个数的商。
l %(取余):计算两个数的余。
以上算术操作符跟数学中相应运算符的用法相同,意义也是一致的。运用这些算术操作符可以很方便地表达对数据的算术运算。例如:
1 + 2; // 对1和2这两个操作数进行加法运算,表达式的值为3 4 * 5; // 对4和5这两个操作数进行乘法运算,表达式的值为20 10 % 7; // 对10除以7取余,表达式的值为3
另外,C++程序中对数值数据的加1或减1操作非常普遍,为了提高编码效率,C++还提供了可以快捷完成此操作的“++(自增)”和“--(自减)”操作符。它们都是一元操作符,可以放在单个操作数的前面或后面(相应地,分别被称为前置操作符或后置操作符),执行对操作数的加1 或减1操作。例如,我们通常用它来实现一些递增和递减的操作:
int nIndex = 0; // … // nIndex自身加1,其值递增为1,等同于nIndex = nIndex + 1 ++nIndex;
最佳实践:使用前置自增操作符代替后置自增操作符
虽然前置和后置自增操作符的意义是相同的,都是对操作数进行加1操作,但当这两种操作符的结果要继续用来参与运算时,它们的效果却是不一样的。观察下面这段代码:
int a = 1; // 定义整型变量a,并给a赋初始值为1 cout<<++a; // 利用前置的自增操作符对a加1,输出为2,这时a的值为2 cout<<a++; // 利用后置的自增操作符对a加1,输出还是2,但是a的值为3
第二条语句的输出为2,这是因为当使用前置自增操作符时,a首先进行自增运算,其数值变为2,然后再输出a的值,自然就是2了。但是大家一定会对第三句输出也为2感到奇怪,为什么a同样执行了自增操作,输出还是2呢?这是因为使用了后置自增操作符,输出语句首先要输出a的当前值2,然后a再进行自增运算,其值变为3。前置操作符是先计算后输出,后置操作符却是先输出后计算,计算顺序的不同才导致了这么奇怪的结果。
既然它们这么容易让人产生困惑,那么,在实际运用中,到底该如何选择呢?可以记住这样一条编程经验:使用前置自增操作符代替后置自增操作符。这样做可以带来如下好处:
1. 前置操作符的效率优于后置操作符
在C++底层,后置操作符是通过前置操作符实现的,实质上,使用后置操作符最终使用的还是前置操作符,并且增加了额外的转换消耗。所以,使用前置操作符可以一定程度上提高代码的执行效率。
2. 前置操作符不易使人产生困惑,增加代码的可读性
后置操作符有时会让人丈二和尚摸不着头脑。例如:
int a = 1; int b = a++ + 1; // 变量b的值到底是2还是3呢?
这段代码执行完成后,b的值是2,而不是3。这是因为这里使用的是后置的自增操作符,它会先把a的当前值1用于计算得到结果2,然后再将其赋值给b并自己增加1变为2。结果虽然简单,可是却难以让人“一眼就看出来”,有时甚至会让人错误地认为结果是3。而如果使用前置操作符,写成:
b = ++a + 1; // 先执行a的自增运算变为2,然后执行加1运算,结果为3
则一眼就可以看出b的值是3,整个代码结果一目了然。
前置的自增操作符可以提高代码的执行效率,同时又增加了代码的可读性,那自然是优先选择使用前置自增操作符了,前置的自减操作符也是同样的道理。
4.1.3 赋值操作符
有了算术操作符,就可以得到各个数据的算术运算结果。但是有了运算结果,还需要把结果保存下来以备后用。而赋值操作符就是用来将结果数据保存到变量的。在C++中,最简单的赋值操作符就是“=”。它是一个二元操作符,其右侧通常是某个常数或表达式,而左侧是某个变量。它的作用就是将右侧的值(如果是表达式,则先计算表达式的值)保存到左侧的变量中,以此实现数据的保存。例如:
int a, b, c; // 将1赋值给变量a,a的值变成1,实现数据1的保存 a = 1; // 连续赋值,首先执行“c = 1”表达式,将c赋值为1, // 然后“c = 1”这个表达式的值为1,继续赋值给b, // b的值也变为1,继续赋值给a,最后a、b、c都被赋值为1 a = b = c = 1; // 先计算“b + 1”表达式的值为2, // 然后继续将其赋值给a,a的值变成2,实现了表达式计算结果的保存 a = b + 1;
另外,赋值操作符左侧的变量有时也会参与右侧表达式的运算。这时,将先以左侧变量的当前值参与右侧表达式的运算,然后再将运算结果赋值给左侧变量。例如:
// 先用a的当前值2进行“a+10”的计算,得到结果12 // 然后再将结果数据保存到a,a的值变为12 a = a + 10; // 先用a的当前值12进行右侧表达式的计算,得到结果24 // 然后再将结果数据保存到a,a的值变为24 a = a * (b + 1);
像这种变量既参与计算又保存结果的赋值操作在C++中非常普遍,为了简化代码,C++还将这些表达式中负责计算的操作符跟赋值操作符结合起来,形成了带计算功能的复合赋值操作符。包括:算术操作符与赋值操作符组合,例如+=、-=、*=、/=、%=;位运算操作符与赋值操作符组合,例如<<=、>>=、&=、^=、|=。
这些复合的赋值操作符同样是二元操作符,它们首先将两侧的操作数按照复合操作符中的算术或位运算操作符进行计算,这时参与计算的是变量的当前值,得到计算结果后再赋值给左侧的操作数,从而在计算的同时完成了赋值,实现了计算和赋值的复合。利用复合赋值操作符,上面的代码就可以简化为:
a += 10; // 等价于表达式 “a = a + 10”,以此实现对a的递增操作 a *= b + 1; // 等价于表达式 “a = a * (b + 1)”