单元表达式 第4单元表达式 本单元教学目标 介绍C++的表达式和表达式语句。 学习要求 熟练掌握C艹+的各种表达式,特别是赋值表达式及其他有副作用的表达式 授课内容 在任何高级程序设计语言中,表达式都是最基本的的组成部分。形式化的表达式定义比 较复杂,需要用到递归定义的概念。简单说来,表达式是由运算符将运算对象(如常数,变 量和函数等)连接起来的具有合法语义的式子。在C艹中,由于运算符比较丰富(达数十种之 多),加之引入了赋值等有副作用的运算符,因而可以构成灵活多样的表达式。这些表达式的 应用一方面可以使程序编写得短小简洁,另一方面还可以完成某些在其他高级程序设计语 言中较难实现的运算功能 学习C++的表达式时应注意以下几个方面 (1)运算符的正确书写方法。C++的许多运算符与通常在数学公式中所见到的符号有很 大差别,例如:整除求余(%),相等(==),逻辑运算与(&&)等等。 今、(2)运算符的确切含义和功能。C艹语言中有一些比较特殊的运算符,有些运算符还有 谓“副作用”,这些都给我们的学习带来了一定的困难 (3)运算符与运算对象的关系。C+的运算符可以分为单目运算符(仅对一个运算对象 进行操作)、双目运算符(需要2个运算对象)甚至还有复合表达式,其中的两个运算符对三 个或者更多个运算对象进行操作 (4)运算符具有优先级和结合方向。如果一个运算对象的两边有不同的运算符,首先执行 优先级别较高的运算。如果一个运算对象两边的运算符级别相同,则应按由左向右的方向顺 序处理。各运算符的优先顺序可以参看表4-3:“运算符的优先级别和结合方向”。如果编程 序时对运算符的优先顺序没有把握,可以通过使用括号来明确其运算顺序 41算术运算符和算术表达式 C++的算术运算符有 +(加),-(减),*(乘),/(除,%(整除求余)
第 4 单元 表达式 - 66 - 第 4 单元 表达式 本单元教学目标 介绍C++的表达式和表达式语句。 学习要求 熟练掌握C++的各种表达式, 特别是赋值表达式及其他有副作用的表达式。 授课内容 在任何高级程序设计语言中, 表达式都是最基本的的组成部分。形式化的表达式定义比 较复杂, 需要用到递归定义的概念。简单说来, 表达式是由运算符将运算对象 (如常数, 变 量和函数等) 连接起来的具有合法语义的式子。在C++中, 由于运算符比较丰富(达数十种之 多), 加之引入了赋值等有副作用的运算符, 因而可以构成灵活多样的表达式。这些表达式的 应用一方面可以使程序编写得短小简洁, 另一方面还可以完成某些在其他高级程序设计语 言中较难实现的运算功能。 学习C++的表达式时应注意以下几个方面: (1) 运算符的正确书写方法。C++的许多运算符与通常在数学公式中所见到的符号有很 大差别, 例如: 整除求余(%), 相等(= =), 逻辑运算与(&&)等等。 (2) 运算符的确切含义和功能。C++语言中有一些比较特殊的运算符, 有些运算符还有 所谓“副作用”, 这些都给我们的学习带来了一定的困难。 (3) 运算符与运算对象的关系。C++的运算符可以分为单目运算符 (仅对一个运算对象 进行操作)、双目运算符(需要 2 个运算对象), 甚至还有复合表达式, 其中的两个运算符对三 个或者更多个运算对象进行操作。 (4)运算符具有优先级和结合方向。如果一个运算对象的两边有不同的运算符, 首先执行 优先级别较高的运算。如果一个运算对象两边的运算符级别相同, 则应按由左向右的方向顺 序处理。各运算符的优先顺序可以参看表 4-3:“运算符的优先级别和结合方向”。如果编程 序时对运算符的优先顺序没有把握, 可以通过使用括号来明确其运算顺序。 4.1 算术运算符和算术表达式 C++的算术运算符有: + (加), − (减), * (乘), / (除), % (整除求余)
第4单元表达式 其中“/”为除法运算符。如果除数和被除数均为整型数据,则结果也是整数。例如,5/3的 结果为1。“%”为整除求余运算符。“%”运算符两侧均应为整型数据,其运算结果为两个 运算对象作除法运算的余数。例如5%3的结果为2。 在C++中,不允许两个算术运算符紧挨在一起,也不能象在数学运算式中那样,任意省 略乘号,以及用中圆点“·”代替乘号等。如果遇到这些情况,应该使用括号将连续的算术 运算符隔开,或者在适当的位置上加上乘法运算符。例如 应写 (x+y)(x-y)应写成(x+y)*(x-y) 42逻辑运算符和逻辑表达式 C++中有6种比较运算符 (大于), <(小于), (等于), =(大于等于),<=(小于等于),!=(不等于 如果比较运算的结果成立,比较表达式取值tue(真),否则比较表达式的值为 false(假)。 在C++中,使用逻辑数据类型(bol)表示逻辑运算的结果l 注意,算术运算符的优先级高于比较运算符 C++中有3种逻辑运算符: !(逻辑非)&&(逻辑与)‖(逻辑或) 在逻辑运算符中,逻辑与“&&”的优先级高于逻辑或“‖”的优先级,而所有的比较运算 符的优先级均高于以上两个逻辑运算符。至于逻辑非运算符“!”,由于这是一个单目运算 符,所以和其他单目运算符(例如用于作正、负号的“+”和“”)一样,优先级高于包括算 术运算符在内的所有双目运算符。例如表达式 x*y>z & x*y<100 -x*y>0 && !isgreat(z) 的运算顺序为 计算x*y /算术运算优先于比较运算 计算x*y>z /比较运算优先于逻辑运算 计算x*y<100 /比较运算优先于逻辑与运算 计算x*y>z&&x*y<100 /逻辑与运算优先于逻辑或运算 计算-x /单目运算优先于双目运算 计算-x*y /算术运算优先于比较运算 1实际上,逻辑表达式的值为整数类型,0表示逻辑值“假”,任何其他非0值都表示逻辑值“真”。在 Visual c艹+中,使用类型助记符bol和逻辑值true, false可使程序易于理解。这些助记符也可写成BOOL TRUE和 FALSE
第 4 单元 表达式 - 67 - 其中“/”为除法运算符。如果除数和被除数均为整型数据, 则结果也是整数。例如, 5/3 的 结果为 1。“%”为整除求余运算符。“%”运算符两侧均应为整型数据, 其运算结果为两个 运算对象作除法运算的余数。例如 5%3 的结果为 2。 在C++中, 不允许两个算术运算符紧挨在一起, 也不能象在数学运算式中那样, 任意省 略乘号, 以及用中圆点“· ”代替乘号等。如果遇到这些情况, 应该使用括号将连续的算术 运算符隔开, 或者在适当的位置上加上乘法运算符。例如 x*−y 应写成 x*(−y) (x+y)(x−y) 应写成 (x+y)*(x−y) 4.2 逻辑运算符和逻辑表达式 C++中有 6 种比较运算符: > (大于), < (小于), == (等于), >= (大于等于), <= (小于等于), != (不等于) 如果比较运算的结果成立, 比较表达式取值 true(真), 否则比较表达式的值为 false(假)。 在C++中,使用逻辑数据类型(bool)表示逻辑运算的结果1。 注意, 算术运算符的优先级高于比较运算符。 C++中有 3 种逻辑运算符: ! (逻辑非) && (逻辑与) || (逻辑或) 在逻辑运算符中,逻辑与“&&”的优先级高于逻辑或“||”的优先级, 而所有的比较运算 符的优先级均高于以上两个逻辑运算符。至于逻辑非运算符“!”, 由于这是一个单目运算 符, 所以和其他单目运算符 (例如用于作正、负号的“+”和“-”)一样, 优先级高于包括算 术运算符在内的所有双目运算符。例如表达式: x*y>z && x*y<100 || -x*y>0 && !isgreat(z) 的运算顺序为: 计算 x*y // 算术运算优先于比较运算 计算 x*y>z // 比较运算优先于逻辑运算 计算 x*y<100 // 比较运算优先于逻辑与运算 计算 x*y>z && x*y<100 // 逻辑与运算优先于逻辑或运算 计算 −x // 单目运算优先于双目运算 计算 −x*y // 算术运算优先于比较运算 1 实际上,逻辑表达式的值为整数类型,0 表示逻辑值“假”, 任何其他非 0 值都表示逻辑值“真”。 在 Visual C++中,使用类型助记符 bool 和逻辑值 true, false 可使程序易于理解。这些助记符也可写成 BOOL, TRUE 和 FALSE
单元表达式 计算-x*y>0 /比较运算优先于逻辑运算 计算 great(z) /计算函数值优先于任何运算符 计算! Isgreat(z) /单目运算优先于双目运算 计算-x*y>0&&! Isgreat(z)//逻辑与运算优先于逻辑或运算 计算x*y》z&&x*y<100|—x*y>0&&! Isgreat(z)2 43赋值运算符和赋值表达式 C艹+将赋值作为一个运算符处理。赋值运算符为“=”,用于构造赋值表达式。赋值表 达式的格式为 其中Ⅴ表示变量,e表示一个表达式。赋值表达式的值等于赋值运算符右边的表达式的值。 其实,赋值表达式的价值主要体现在其副作用上,即赋值运算符可以改变作为运算对象的变 量V的值。赋值表达式的副作用就是将计算出来的表达式e的值存入变量V 和其他表达式一样,赋值表达式也可以作为更复杂的表达式的组成部分。例如 由于赋值运算符的优先级较低(仅比逗号运算符高,见自学部分)。并列的赋值运算符 之间的结合方向为从右向左,所以上述语句的执行顺序是:首先计算出表达式mn的值,然 后再处理表达式j=m*n,该表达式的值就是m*n的值,其副作用为将该值存入变量j。最后 处理表达式i=j=m*n,其值即第一个赋值运算符右面的整个表达式的值,因此也就是mn 的值(最后计算出的这一赋值表达式的值并没有使用),其副作用为将第一个赋值运算符右 面整个表达式的值存入变量i。因此,上述表达式语句的作用是将mn的值赋给变量i和j 整个运算过程如下(设m的值为2,n的值为3) 计算m*n的值 2*3等于6; 计算j=m*n的值:j=6的值等于6,其副作用为将6存入变量j; 计算i=j=m*n的值 6的值等于6,其副作用为将6存入变量 44自增运算符和自减运算符 C++中有两个很有特色的运算符:自增运算符“+”和自减运算符“-”。这两个运算 符也是C++程序中最常用的运算符,以致于它们几乎成为C++程序的象征 艹+”和“—”运算符都是单目运算符,且其运算对象只能是整型变量或指针变量。这 实际的运算顺序与这里介绍的略有区别,主要是因为C+语言在执行表达式时进行了优化工作。例如,对 于表达式x*y>z&&x*y<100来说,如果第一个比较表达式x"y>z不成立(等于0),则无论第二个表达式 x*y<100成立或不成立,整个表达的值均为0不成立),因此就无需计算第二个表达式
第 4 单元 表达式 - 68 - 计算 −x*y>0 // 比较运算优先于逻辑运算 计算 isgreat(z) // 计算函数值优先于任何运算符 计算 !isgreat(z) // 单目运算优先于双目运算 计算 −x*y>0 && !isgreat(z) // 逻辑与运算优先于逻辑或运算 计算 x*y>z && x*y<100 || −x*y>0 && !isgreat(z) 2 4.3 赋值运算符和赋值表达式 C++将赋值作为一个运算符处理。赋值运算符为“=”,用于构造赋值表达式。赋值表 达式的格式为: V = e 其中 V 表示变量, e 表示一个表达式。赋值表达式的值等于赋值运算符右边的表达式的值。 其实, 赋值表达式的价值主要体现在其副作用上, 即赋值运算符可以改变作为运算对象的变 量 V 的值。赋值表达式的副作用就是将计算出来的表达式 e 的值存入变量 V。 和其他表达式一样,赋值表达式也可以作为更复杂的表达式的组成部分。例如 i = j = m*n; 由于赋值运算符的优先级较低(仅比逗号运算符高, 见自学部分)。并列的赋值运算符 之间的结合方向为从右向左, 所以上述语句的执行顺序是: 首先计算出表达式 m*n 的值, 然 后再处理表达式 j = m*n, 该表达式的值就是 m*n 的值, 其副作用为将该值存入变量 j。最后, 处理表达式 i = j = m*n, 其值即第一个赋值运算符右面的整个表达式的值, 因此也就是 m*n 的值 (最后计算出的这一赋值表达式的值并没有使用), 其副作用为将第一个赋值运算符右 面整个表达式的值存入变量 i。因此,上述表达式语句的作用是将 m*n 的值赋给变量 i 和 j。 整个运算过程如下 (设 m 的值为 2,n 的值为 3): 计算 m*n 的值: 2*3 等于 6; 计算 j = m*n 的值 : j = 6 的值等于 6, 其副作用为将 6 存入变量 j; 计算 i = j = m*n 的值: i = 6 的值等于 6, 其副作用为将 6 存入变量 i。 4.4 自增运算符和自减运算符 C++中有两个很有特色的运算符: 自增运算符“++”和自减运算符“−−”。这两个运算 符也是C++程序中最常用的运算符, 以致于它们几乎成为C++程序的象征。 “++”和“−−”运算符都是单目运算符, 且其运算对象只能是整型变量或指针变量。这 2 实际的运算顺序与这里介绍的略有区别, 主要是因为C++语言在执行表达式时进行了优化工作。例如, 对 于表达式 x*y>z && x*y<100 来说, 如果第一个比较表达式 x*y>z 不成立(等于 0), 则无论第二个表达式 x*y<100 成立或不成立, 整个表达的值均为 0(不成立), 因此就无需计算第二个表达式
单元表达式 两个运算符既可以放在作为运算对象的变量之前,也可以放在变量之后。这四种表达式的值 分别为 ++的值和i的值相同 i—-的值和i的值相同 的值为i+1 i的值为i-1。 然而,“++”和“-”这两个运算符真正的价值在于它们和赋值运算符类似,在参加运 算的同时还改变了作为运算对象的变量的值。++和计+会使变量i的值增大1;类似地 和ⅰ-会使变量i的值减1。因此,考虑到副作用以后,“++”和“-”构成的4种表达式的含 义见表4-1(设i为一整型变量)。 表41自增运算符和自减运算符的用法 表达式 表达式的值 副作用 1+ i的值增大1 ++1 i+1 i的值增大1 i的值减少1 i的值减少1 ++”表达式和“-”表达式既可以单独使用,也可以出现于更复杂的表达式中。例如 /i增加 /i减少1 x=aray[++i];〃/将aray[i+1]的值赋给x,并使i增加1 sl[i++]=s2[j++];∥/将s2[j赋给sl[i],然后分别使i和j增加1 作为运算符来说“艹+”和“—-”的优先级较高高于所有算术运算符和逻辑运算符。但 在使用这两个运算符时要注意它们的运算对象只能是变量,不能是其他表达式。例如, (计+j)++就是一个错误的表达式。 引入含有“++”、“-”以及赋值运算符这类有副作用的表达式的目的在于简化程序的 编写。例如,表达式语句i=j=m*n,的作用和 J=m*n 完全一样,而表达式语句sl[计++]=s2[+,其实正是下列语句的简化表达方式 l[i] s2[j] i=i+1 例4字符串连接 算法:所谓字符串连接,就是将两个字符串合并成一个新的字符串。函数 strato 可以实现字符串连接的功能,其具体做法是将第二个字符串的内容复制到第一个字符串的
第 4 单元 表达式 - 69 - 两个运算符既可以放在作为运算对象的变量之前, 也可以放在变量之后。这四种表达式的值 分别为: i++ 的值和 i 的值相同; i−− 的值和 i 的值相同; ++i 的值为 i+1; −−i 的值为 i-1。 然而, “++”和“−−”这两个运算符真正的价值在于它们和赋值运算符类似, 在参加运 算的同时还改变了作为运算对象的变量的值。++i 和 i++会使变量 i 的值增大 1; 类似地, −−i 和 i−−会使变量 i 的值减 1。因此,考虑到副作用以后,“++”和“−−”构成的 4 种表达式的含 义见表 4-1(设 i 为一整型变量)。 表 4-1 自增运算符和自减运算符的用法 表达式 表达式的值 副作用 i++ ++i i-- --i i i+1 i i-1 i 的值增大 1 i 的值增大 1 i 的值减少 1 i 的值减少 1 ++”表达式和“−−”表达式既可以单独使用, 也可以出现于更复杂的表达式中。例如 i++; // i 增加 1 −−i; // i 减少 1 x = array[++i]; // 将 array[i+1]的值赋给 x, 并使 i 增加 1 s1[i++] = s2[j++]; // 将 s2[j]赋给 s1[i], 然后分别使 i 和 j 增加 1 作为运算符来说,“++”和“−−”的优先级较高,高于所有算术运算符和逻辑运算符。但 在使用这两个运算符时要注意它们的运算对象只能是变量, 不能是其他表达式。例如, (i+j)++就是一个错误的表达式。 引入含有“++”、“−−”以及赋值运算符这类有副作用的表达式的目的在于简化程序的 编写。例如, 表达式语句 i = j = m*n; 的作用和 j = m*n; i = j; 完全一样; 而表达式语句 s1[i++] = s2[j++]; 其实正是下列语句的简化表达方式: s1[i] = s2[j]; i = i+1; j = j+1; [例 4-1] 字符串连接。 算 法: 所谓字符串连接, 就是将两个字符串合并成一个新的字符串。函数 mstrcat() 可以实现字符串连接的功能, 其具体做法是将第二个字符串的内容复制到第一个字符串的
第4单元表达式 尾部去 程序 ∥ Example4-1:连接两个字符串 void mstrcat(char destin[, char source[) int 1 while(source [j]!=0) destin[i++]= source [j++] destin[i] =0 分析:函数 mistreat()将字符串 source的内容复制在字符串 destin的后面。显然,字 符型数组 destin的大小一定要能够容纳得下连接后的新字符串才行。在复制工作开始之前 首先要确定在 destin中的复制位置,这是通过库函数 strlen(完成的 自学内容 45其他具有副作用的运算符 除了“++”和“-”以外,在C++中还有其他一些有副作用的复合运算符,它们都是以 称为赋值运算符“=”为基础构成的。例如算术复合赋值运算符“+=”,“-=”,“*=”,“仁=” 以及“%=”;另外还有用位运算符和赋值运算符复合而成的复合赋值运算符(见4.8:“位运 算表达式”)。算术复合运算符的形式为 V+=e的含义为将表达式e的值加在变量V上; V-=e的含义为将表达式e的值从变量V中减去 V*=e的含义为将变量V与表达式e的乘积存入变量V中 V/=e的含义为将变量V和表达式e的商存入变量V中; V%=e的含义为将变量V和表达式e的余数存入变量V中。 C+引入了一些有副作用的表达式,一方面丰富了程序的表达方式,使得C++程序的 形式简洁、干练;生成的目标代码的效率也比较高。但另一方面这些表达式比较复杂,难于 理解和调试,有时还会因为不同的C艹+编译程序对计算顺序的规定不同而产生二义性的解 释。因此在编程时要慎重使用。为了确保实现自己所要求的计算顺序,可以通过加括号的方 法加以明确;甚至可以将由多个有副作用的表达式组成的复杂表达式语句分解成几个比较 简单的表达式语句处理
第 4 单元 表达式 - 70 - 尾部去。 程 序 // Example 4-1:连接两个字符串 void mstrcat(char destin[], char source[]) { int i = strlen(destin), j = 0; while(source[j]!=0) destin[i++] = source[j++]; destin[i] = 0; } 分 析: 函数 mstrcat()将字符串 source 的内容复制在字符串 destin 的后面。显然, 字 符型数组 destin 的大小一定要能够容纳得下连接后的新字符串才行。在复制工作开始之前, 首先要确定在 destin 中的复制位置, 这是通过库函数 strlen()完成的。 自学内容 4.5 其他具有副作用的运算符 除了“++”和“−−”以外, 在C++中还有其他一些有副作用的复合运算符, 它们都是以 称为赋值运算符“=”为基础构成的。例如算术复合赋值运算符“+=”,“−=”,“*=”,“/=” 以及“%=”; 另外还有用位运算符和赋值运算符复合而成的复合赋值运算符(见 4.8:“位运 算表达式”)。算术复合运算符的形式为: V += e 的含义为将表达式 e 的值加在变量 V 上; V −= e 的含义为将表达式 e 的值从变量 V 中减去; V *= e 的含义为将变量 V 与表达式 e 的乘积存入变量 V 中; V /= e 的含义为将变量 V 和表达式 e 的商存入变量 V 中; V %= e 的含义为将变量 V 和表达式 e 的余数存入变量 V 中。 C++引入了一些有副作用的表达式, 一方面丰富了程序的表达方式, 使得C++程序的 形式简洁、干练; 生成的目标代码的效率也比较高。但另一方面这些表达式比较复杂, 难于 理解和调试, 有时还会因为不同的C++编译程序对计算顺序的规定不同而产生二义性的解 释。因此在编程时要慎重使用。为了确保实现自己所要求的计算顺序, 可以通过加括号的方 法加以明确; 甚至可以将由多个有副作用的表达式组成的复杂表达式语句分解成几个比较 简单的表达式语句处理