重要的事情说三遍:
介绍了变量和常量之后,我们可以开始使用 运算符 对它们进行操作。以下是运算符的完整列表。目前,可能不需要知道所有的运算符,但它们都列在这里,以便作为参考。
赋值运算符 (=
)
赋值运算符用于将值赋给变量。
x = 5;
这条语句将整数值5
赋给变量x
。赋值操作总是从右到左进行,从不会相反:
x = y;
这条语句将变量y
的值赋给变量x
。在执行此语句时,x
当前的值将丢失并被y
的值替代。
请注意,我们只是在赋值操作时将y
的值赋给x
。因此,如果稍后y
发生变化,它不会影响x
的新值。
例如,看看以下代码——我在注释中包含了变量存储内容的演变:
// 赋值运算符
#include <iostream>
using namespace std;
int main ()
{
int a, b; // a:?, b:?
a = 10; // a:10, b:?
b = 4; // a:10, b:4
a = b; // a:4, b:4
b = 7; // a:4, b:7
cout << "a:";
cout << a;
cout << " b:";
cout << b;
}
此程序在屏幕上打印了a
和b
的最终值(分别为4和7)。注意,即使我们早先声明了a = b
,a
并未受到b
最终修改的影响。
赋值操作是可以求值的表达式。这意味着赋值本身有一个值,对于基本类型来说,这个值就是赋值操作中赋给的值。例如:
y = 2 + (x = 5);
在这个表达式中,y
被赋值为2加上另一个赋值表达式的值(其自身的值为5)。它大致等价于:
x = 5;
y = 2 + x;
最终结果是将7赋值给y
。
以下表达式在C++中也是有效的:
x = y = z = 5;
它将5赋值给三个变量:x
、y
和z
,总是从右到左进行。
算术运算符 ( +, -, *, /, %
)
C++支持的五种算术运算是:
运算符 | 描述 |
---|---|
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 取模(求余) |
加法、减法、乘法和除法运算对应于各自的数学运算符。最后一个,取模运算符,用百分号(%
)表示,给出两个值相除的余数。例如:
x = 11 % 3;
结果是变量x
包含值2,因为11除以3的结果是3,余数是2。
复合赋值运算符 (+=, -=, *=, /=, %=, >>=, <<=, &=, ^=, |=
)
复合赋值运算符通过对变量执行操作来修改其当前值。它们等同于将操作结果赋给第一个操作数:
表达式 | 等同于… |
---|---|
y += x; | y = y + x; |
x -= 5; | x = x - 5; |
x /= y; | x = x / y; |
price *= units + 1; | price = price * (units+1) |
对于所有其他复合赋值运算符也是如此。例如:
// 复合赋值运算符
#include <iostream>
using namespace std;
int main ()
{
int a, b=3;
a = b;
a+=2; // 等同于 a=a+2
cout << a;
}
增量和减量运算符 (++, --
)
一些表达式可以进一步缩短:增加运算符(++
)和减少运算符(--
)分别将变量存储的值增加或减少1。它们分别等同于+=1
和-=1
。因此:
++x;
x+=1;
x=x+1;
这三种表达式在功能上是等价的;它们都将x
的值增加1。
在早期的C编译器中,前面三种表达式可能会根据使用的哪一种生成不同的可执行代码。如今,这种类型的代码优化通常由编译器自动执行,因此这三种表达式应生成完全相同的可执行代码。
这种运算符的一个特点是可以作为前缀和后缀使用。这意味着它可以写在变量名之前(++x
)或之后(x++
)。虽然在简单表达式如x++
或++x
中,两者具有完全相同的意义;但在其他表达式中,增量或减量操作的结果被求值时,它们可能在意义上有重要区别:如果增加运算符用作值的前缀(++x
),表达式求值为增加后的x
的最终值。而如果用作后缀(x++
),值也会增加,但表达式求值为增加前的x
的值。注意区别:
示例1 | 示例2 |
---|---|
x = 3; y = ++x; // x包含4,y包含4 | x = 3; y = x++; // x包含4,y包含3 |
在 示例1 中,赋给y
的值是增加后的x
的值。而在 示例2 中,是增加前x
的值。
关系运算符和比较运算符 (==, !=, >, <, >=, <=
)
两个表达式可以使用关系和相等运算符进行比较。例如,知道两个值是否相等或一个值是否大于另一个值。
这种运算的结果是true
或false
(即布尔值)。
C++中的关系运算符如下:
运算符 | 描述 |
---|---|
== | 等于 |
!= | 不等于 |
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
以下是一些示例:
(7 == 5) // 结果为 false
(5 > 4) // 结果为 true
(3 != 2) // 结果为 true
(6 >= 6) // 结果为 true
(5 < 5) // 结果为 false
当然,不仅仅是数值常量可以比较,任何值都可以比较,包括变量。例如,假设a=2
、b=3
和c=6
,则:
(a == 5) // 结果为 false,因为a不等于5
(a*b >= c) // 结果为 true,因为 (2*3 >= 6) 为 true
(b+4 > a*c) // 结果为 false,因为 (3+4 > 2*6) 为 false
((b=2) == a) // 结果为 true
注意!赋值运算符(运算符=
,一个等号)与相等比较运算符(运算符==
,两个等号)不同;前者(=
)将右侧的值赋给左侧的变量,而后者(==
)比较运算符两侧的值是否相等。因此,在最后一个表达式((b=2) == a
)中,我们首先将值2
赋给b
,然后将其与a
进行比较(a
也存储值2),结果为true
。
逻辑运算符 (!, &&, ||
)
运算符!
是C++中的布尔运算符NOT。它只有一个操作数,在其右侧,并反转其值,如果其操作数为true
,则结果为false
,如果其操作数为false
,则结果为true
。基本上,它返回其操作数求值的相反布尔值。例如:
!(5 == 5) // 结果为 false
,因为其右侧的表达式 (5 == 5) 为 true
!(6 <= 4) // 结果为 true,因为 (6 <= 4) 为 false
!true // 结果为 false
!false // 结果为 true
逻辑运算符&&
和||
用于评估两个表达式以获得单一的关系结果。运算符&&
对应布尔逻辑运算AND,当且仅当其两个操作数均为true
时,结果才为true
,否则为false
。以下面板显示了运算符&&
求值表达式a&&b
的结果:
&&
运算符:
a | b | a && b |
---|---|---|
true | true | true |
true | false | false |
false | true | false |
false | false | false |
运算符||
对应布尔逻辑运算OR,只要其两个操作数中的任意一个为true
,结果即为true
,因此只有在两个操作数均为false
时才为false
。以下是a||b
的可能结果:
||
运算符:
a | b | a || b |
---|---|---|
true | true | true |
true | false | true |
false | true | true |
false | false | false |
例如:
( (5 == 5) && (3 > 6) ) // 结果为 false ( true && false )
( (5 == 5) || (3 > 6) ) // 结果为 true ( true || false )
使用逻辑运算符时,C++只从左到右评估必要的部分以得出组合的关系结果,忽略其余部分。因此,在最后一个示例((5==5)||(3>6)
)中,C++首先评估5==5
是否为true
,如果是,则不会检查3>6
是否为true
。这称为 短路求值 ,对于这些运算符的工作原理如下:
运算符 | 短路求值 |
---|---|
&& | 如果左侧表达式为false ,则组合结果为false (右侧表达式不再被评估)。 |
|| | 如果左侧表达式为true ,则组合结果为true (右侧表达式不再被评估)。 |
当右侧表达式具有副作用(如改变值)时,这一点尤为重要:
if ( (i<10) && (++i<n) ) { /*...*/ } // 注意,条件会增加i的值
这里,组合条件表达式会增加i
的值,但仅在左侧&&
的条件为true
时,因为否则右侧条件(++i<n
)不会被评估。
条件(三元)运算符 (?
)
条件运算符评估一个表达式,如果该表达式求值为true
,则返回一个值,如果求值为false
,则返回另一个值。其语法为:
condition ? result1 : result2
如果condition
为true
,则整个表达式求值为result1
,否则求值为result2
。
7==5 ? 4 : 3 // 结果为 3,因为7不等于5。
7==5+2 ? 4 : 3 // 结果为 4,因为7等于5+2。
5>3 ? a : b // 结果为a的值,因为5大于3。
a>b ? a : b // 结果为a或b中较大的一个。
例如:
// 条件运算符
#include <iostream>
using namespace std;
int main ()
{
int a,b,c;
a=2;
b=7;
c = (a>b) ? a : b;
cout << c << '\n';
}
在此示例中,a
是2,b
是7,因此评估的表达式(a>b
)不为true
,因此丢弃问号后的第一个值,选择第二个值(冒号后的值),即b
(值为7)。
逗号运算符 ( ,
)
逗号运算符(,
)用于分隔两个或多个表达式,这些表达式包含在只期望一个表达式的位置。当需要对一组表达式进行求值时,只考虑最右侧的表达式。
例如,以下代码:
a = (b=3, b+2);
首先将值3赋给b
,然后将b+2
赋给变量a
。因此,最终变量a
包含值5,而变量b
包含值3。
位运算符 ( &, |, ^, ~, <<, >>
)
位运算符在修改变量时考虑它们存储值的位模式。
运算符 | 汇编等价物 | 描述 |
---|---|---|
& | AND | 位与 |
| | OR | 位或 |
^ | XOR | 位异或 |
~ | NOT | 一元补码(位取反) |
<< | SHL | 左移位 |
>> | SHR | 右移位 |
显式类型转换运算符
类型转换运算符允许将给定类型的值转换为另一种类型。在C++中,有几种方法可以做到这一点。最简单的方法是继承自C语言的,在要转换的表达式前加上用括号括起来的新类型:
int i;
float f = 3.14;
i = (int) f;
上面的代码将浮点数3.14
转换为整数值(3
);余数被舍弃。这里,类型转换运算符是(int)
。在C++中做同样事情的另一种方式是使用函数符号表示法,将要转换的表达式前置类型并将表达式括在括号中:
i = int (f);
这两种类型转换的方法在C++中都是有效的。
sizeof
该运算符接受一个参数,可以是类型或变量,并返回该类型或对象的字节大小:
x = sizeof (char);
这里,x
被赋值为1
,因为char
类型的大小为一个字节。
sizeof
返回的值是一个编译时常量,因此总是在程序执行之前确定。
其他运算符
在这些教程的后面章节中,我们将看到一些更多的运算符,例如涉及指针的运算符或面向对象编程的特定运算符。
运算符优先级
一个表达式可能包含多个运算符。例如:
x = 5 + 7 % 2;
在C++中,上述表达式总是将6赋给变量x
,因为%
运算符的优先级高于+
运算符,并且总是先计算。可以使用括号将表达式的部分括起来以覆盖这种优先级顺序,或明确表达预期的效果。注意区别:
x = 5 + (7 % 2); // x = 6(与没有括号的情况相同)
x = (5 + 7) % 2; // x = 0
从最高到最低优先级,C++运算符按以下顺序进行求值:
级别 | 优先级组 | 运算符 | 描述 | 结合性 |
---|---|---|---|---|
1 | 作用域 | :: | 作用域限定符 | 从左到右 |
2 | 后缀(单目) | ++ -- | 后缀递增 / 递减 | 从左到右 |
() | 函数调用形式 | 从左到右 | ||
[] | 下标 | 从左到右 | ||
-> | 成员访问 | 从左到右 | ||
3 | 前缀(单目) | ++ -- | 前缀递增 / 递减 | 从右到左 |
! ~ | 逻辑非 / 位非 | 从右到左 | ||
+ - | 单目前缀 | 从右到左 | ||
& * | 引用 / 解引用 | 从右到左 | ||
new delete | 分配 / 释放 | 从右到左 | ||
sizeof | 参数包 | 从右到左 | ||
(type) | C风格类型转换 | 从右到左 | ||
4 | 指针到成员 | .* ->* | 访问指针 | 从左到右 |
5 | 算术:缩放 | * / % | 乘法,除法,取模 | 从左到右 |
6 | 算术:加法 | + - | 加法,减法 | 从左到右 |
7 | 位移 | << >> | 左移,右移 | 从左到右 |
8 | 关系运算 | < > <= >= | 比较运算符 | 从左到右 |
9 | 相等运算 | == != | 相等 / 不相等 | 从左到右 |
10 | 位与 | & | 位与 | 从左到右 |
11 | 位异或 | ^ | 位异或 | 从左到右 |
12 | 位或 | | | 位或 | 从左到右 |
13 | 逻辑与 | && | 逻辑与 | 从左到右 |
14 | 逻辑或 | || | 逻辑或 | 从左到右 |
15 | 赋值级表达式 | = *= /= %= += -= <<= >>= &= ^= |= | 赋值 / 复合赋值 | 从右到左 |
?: | 条件运算符 | 从右到左 | ||
16 | 序列 | , | 逗号分隔符 | 从左到右 |
当一个表达式具有两个相同优先级级别的运算符时,分组 决定先评估哪个:左到右或右到左。
将所有子语句括在括号中(即使是由于优先级而不必要的括号)可以提高代码的可读性。