4.2 算术运算符
|
表4.1:算术运算符(左结合律)
|
|
运算符
|
功能
|
用法
|
|
+
|
一元正号
|
+ expr
|
|
-
|
一元负号
|
- expr
|
|
*
|
乘法
|
expr * expr
|
|
/
|
除法
|
expr / expr
|
|
%
|
求余
|
expr % expr
|
|
+
|
加法
|
expr + expr
|
|
-
|
减法
|
expr – expr
|
表4.1(以及后面章节的运算符表)按照运算符的优先级将其分组。一元运算符的优先级最高,接下来是乘法和除法,优先级最低的是加法和减法。优先级高的运算符比优先级低的运算符组合得更紧密。上面的所有运算符都满足左结合律,意味着当优先级相同时按照从左向右的顺序进行组合。
除非另做特殊说明,算术运算符都能作用于任意算术类型(参见2.1.1节,第32页)以及任意能转换为算术类型的类型。算术运算符的运算对象和求值结果都是右值。如4.11节(第159页)描述的那样,在表达式求值之前,小整数类型的运算对象被提升成较大的整数类型,所有运算对象最终会转换成同一类型。
一元正号运算符、加法运算符和减法运算符都能作用于指针。3.5.3节(第119页)已经介绍过二元加法和减法运算符作用于指针的情况。当一元正号运算符作用于一个指针或者算术值时,返回运算对象值的一个(提升后的)副本。
一元负号运算符对运算对象值取负后,返回其(提升后的)副本:
- int i = 1024;
- int k = -i; // k是-1024
- bool b = true;
-
- bool b2 = -b; // b2是true!
在2.1.1节(第34页),我们指出布尔值不应该参与运算,-b就是一个很好的例子。
对大多数运算符来说,布尔类型的运算对象将被提升为int类型。如上所示,布尔变量b的值为真,参与运算时将被提升成整数值1(参见2.1.2节,第35页),对它求负后的结果是-1。将-1再转换回布尔值并将其作为b2的初始值,显然这个初始值不等于0,转换成布尔值后应该为1。所以,b2的值是真!
提示:溢出和其他算术运算异常
算术表达式有可能产生未定义的结果。一部分原因是数学性质本身:例如除数是0的情况;另外一部分则源于计算机的特点:例如溢出,当计算的结果超出该类型所能表示的范围时就会产生溢出。
假设某个机器的short类型占16位,则最大的short数值是32767。在这样一台机器上,下面的复合赋值语句将产生溢出:
- short short_value = 32767;// 如果short类型占16位,则能表示的最大值是32767
- short_value += 1; // 该计算导致溢出
- cout << "short_value: " << short_value << endl;
- 给short_value赋值的语句是未定义的,这是因为表示一个带符号
给short_value赋值的语句是未定义的,这是因为表示一个带符号数32768需要17位,但是short类型只有16位。很多系统在编译和运行时都不报溢出错误,像其他未定义的行为一样,溢出的结果是不可预知的。在我们的系统中,程序的输出结果是:
- short_value: -32768
该值发生了"环绕(wrapped around)",符号位本来是0,由于溢出被改成了1,于是结果变成一个负值。在别的系统中也许会有其他结果,程序的行为可能不同甚至直接崩溃。
当作用于算术类型的对象时,算术运算符+、 、*、/的含义分别是加法、减法、乘法和除法。整数相除结果还是整数,也就是说,如果商含有小数部分,直接弃除:
- int ival1 = 21/6; // ival1是3,结果进行了删节,余数被抛弃掉了
- int ival2 = 21/7; // ival2 是3,没有余数,结果是整数值
运算符%俗称"取余"或"取模"运算符,负责计算两个整数相除所得的余数,参与取余运算的运算对象必须是整数类型:
- int ival = 42;
- double dval = 3.14;
- ival % 12; // 正确:结果是6
- ival % dval; // 错误:运算对象是浮点类型
在除法运算中,如果两个运算对象的符号相同则商为正(如果不为0的话),否则商为负。C++(www.cppentry.com)语言的早期版本允许结果为负值的商向上或向下取整,C++(www.cppentry.com)11新标准则规定商一律向0取整(即直接切除小数部分)。
根据取余运算的定义,如果m和n是整数且n非0,则表达式(m/n)*n+m%n的求值结果与m相等。隐含的意思是,如果m%n不等于0,则它的符号和m相同。C++(www.cppentry.com)语言的早期版本允许m%n的符号匹配n的符号,而且商向负无穷一侧取整,这一方式在新标准中已经被禁止使用了。除了 m导致溢出的特殊情况,其他时候(-m)/n和m/(-n)都等于-(m/n),m%(-n)等于m%n,(-m)%n等于-(m%n)。具体示例如下:
- 21 % 6; /* 结果是3 */ 21 / 6; /* 结果是3 */
- 21 % 7; /* 结果是0 */ 21 / 7; /* 结果是3 */
- -21 % -8; /* 结果是-5 */ -21 / -8; /* 结果是2 */
- 21 % -5; /* 结果是1 */ 21 / -5; /* 结果是-4 */
4.2节练习
练习4.4:在下面的表达式中添加括号,说明其求值的过程及最终结果。编写程序编译该(不加括号的)表达式并输出其结果验证之前的推断。
12 / 3 * 4 + 5 * 15 + 24 % 4 / 2
练习4.5:写出下列表达式的求值结果。
(a) -30 * 3 + 21 / 5 (b) -30 + 3 * 21 / 5
(c) 30 / 3 * 21 % 5 (d) -30 / 3 * 21 % 4
练习4.6:写出一条表达式用于确定一个整数是奇数还是偶数。
练习4.7:溢出是何含义?写出三条将导致溢出的表达式。