运算符

在介绍了变量和常量后,我们就可以开始使用运算符来对它们进行操作了。下面是运算符的完整列表。目前,可能不需要了解所有运算符,但此处列出所有运算符是为了方便查阅。

赋值运算符(=)

赋值运算符将一个值赋给一个变量。

1
x = 5;

此语句将整数值5赋给变量x。赋值操作始终从右到左进行,绝不会反向操作。

1
x = y;

此语句将变量y中的值赋给变量x。执行此语句时x的当前值将被丢弃,并被y的值替换。

另外请注意,我们只是在赋值操作时将y的值赋给了x。因此,如果y在稍后发生更改,也不会影响x的新值。

例如,让我们看一下以下代码——我在注释中包含了变量中存储内容的变化情况。

// assignment operator
#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:4 b:7

此程序将在屏幕上打印ab的最终值(分别为4和7)。请注意,即使我们之前声明了a = ba也没有受到b最终修改的影响。

赋值操作是表达式,可以进行求值。这意味着赋值本身有一个值,并且对于基本类型,该值是在操作中赋的值。例如:

1
y = 2 + (x = 5);

在此表达式中,y被赋值为2与另一个赋值表达式(该表达式本身的值为5)相加的结果。它大致等同于:

1
2
x = 5;
y = 2 + x;

最终结果是将7赋给y

以下表达式在C++中也是有效的:

1
x = y = z = 5;

它将5赋给了所有三个变量:xyz;始终从右到左赋值。

算术运算符(+、-、*、/、%)

C++支持的五种算术运算是:

运算符描述
+加法 (addition)
-subtraction (减法)
*乘法
/除法
%取模

加法、减法、乘法和除法运算分别与其对应的数学运算符字面意义相同。最后一个取模运算符,用百分号(%)表示,给出两个值相除的余数。例如:

1
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);

其他复合赋值运算符也是如此。例如:

1
2
3
4
5
6
7
8
9
10
11
// compound assignment operators
#include <iostream>
using namespace std;

int main ()
{
  int a, b=3;
  a = b;
  a+=2;             // equivalent to a=a+2
  cout << a;
}
5

自增自减运算符(++、--)

一些表达式可以缩短:自增运算符(++)和自减运算符(--)将变量中存储的值增加或减少一。它们分别等同于+=1-=1。因此:

1
2
3
++x;
x+=1;
x=x+1;

在功能上都是等效的;它们三个都将x的值加一。

在早期的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增加之前的值。

关系运算符和比较运算符(==、!=、>、<、>=、<=)

可以使用关系运算符和相等运算符来比较两个表达式。例如,判断两个值是否相等,或者一个值是否大于另一个值。

这种操作的结果是真或假(即布尔值)。

C++中的关系运算符是:

运算符描述
==等于
!=不等于
<小于
>大于
<=小于或等于
>=大于或等于

这里有一些例子:

1
2
3
4
5
(7 == 5)     // evaluates to false
(5 > 4)      // evaluates to true
(3 != 2)     // evaluates to true
(6 >= 6)     // evaluates to true
(5 < 5)      // evaluates to false 

当然,不仅可以比较数字常量,还可以比较任何值,包括变量。假设a=2b=3c=6,则:

1
2
3
4
(a == 5)     // evaluates to false, since a is not equal to 5
(a*b >= c)   // evaluates to true, since (2*3 >= 6) is true
(b+4 > a*c)  // evaluates to false, since (3+4 > 2*6) is false
((b=2) == a) // evaluates to true 

请注意!赋值运算符(=,一个等号)与相等比较运算符(==,两个等号)不同;第一个(=)将右侧的值赋给其左侧的变量,而第二个(==)则比较运算符两侧的值是否相等。因此,在最后一个表达式((b=2) == a)中,我们首先将值2赋给了b,然后将其与a(也存储着值2)进行了比较,结果为true

逻辑运算符(!、&&、||)

!运算符是C++的布尔运算NOT运算符。它只有一个操作数,位于其右侧,并对其进行取反,如果其操作数为true则产生false,如果其操作数为false则产生true。基本上,它返回其操作数求值结果的相反布尔值。例如:

1
2
3
4
!(5 == 5)   // evaluates to false because the expression at its right (5 == 5) is true
!(6 <= 4)   // evaluates to true because (6 <= 4) would be false
!true       // evaluates to false
!false      // evaluates to true 

当对两个表达式求值以获得单个关系结果时,使用逻辑运算符&&||&&运算符对应于布尔逻辑运算AND,当两个操作数均为true时为true,否则为false。以下面板显示了运算符&&求值表达式a&&b的结果:

&& 运算符(AND)
a和 ba && b

||运算符对应于布尔逻辑运算OR,当任一操作数为true时为true,仅当两个操作数都为false时才为false。以下是a||b的可能结果:

|| 运算符(OR)
a和 ba || b

例如:
1
2
( (5 == 5) && (3 > 6) )  // evaluates to false ( true && false )
( (5 == 5) || (3 > 6) )  // evaluates to true ( true || false ) 

在使用逻辑运算符时,C++从左到右仅评估必要的部分以得出组合关系结果,忽略其余部分。因此,在最后一个示例((5==5)||(3>6))中,C++首先评估5==5是否为true,如果是,则从不检查3>6是否为true。这被称为短路求值,对于这些运算符的工作方式如下:

运算符短路
&&如果左侧表达式为false,则组合结果为false(右侧表达式永远不求值)。
||如果左侧表达式为true,则组合结果为true(右侧表达式永远不求值)。

当右侧表达式具有副作用时,例如修改值,这一点尤其重要。

1
if ( (i<10) && (++i<n) ) { /*...*/ }   // note that the condition increments i 

在这里,组合条件表达式会将i加一,但这仅在&&左侧的条件为true时发生,因为否则,右侧的条件(++i<n)永远不会被求值。

条件三元运算符(?)

条件运算符对一个表达式进行求值,如果该表达式求值为true,则返回一个值,如果表达式求值为false,则返回另一个值。其语法是:

condition ? result1 : result2

如果conditiontrue,则整个表达式求值为result1,否则为result2

1
2
3
4
7==5 ? 4 : 3     // evaluates to 3, since 7 is not equal to 5.
7==5+2 ? 4 : 3   // evaluates to 4, since 7 is equal to 5+2.
5>3 ? a : b      // evaluates to the value of a, since 5 is greater than 3.
a>b ? a : b      // evaluates to whichever is greater, a or b.  

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// conditional operator
#include <iostream>
using namespace std;

int main ()
{
  int a,b,c;

  a=2;
  b=7;
  c = (a>b) ? a : b;

  cout << c << '\n';
}
7

在此示例中,a为2,b为7,因此被求值的表达式(a>b)不为true,因此将问题标记后的第一个值(冒号后的值)b(值为7)所替换。

逗号运算符(,)

逗号运算符(,)用于分隔两个或多个表达式,这些表达式包含在只能有一个表达式的地方。当需要对一组表达式进行求值以获得一个值时,只考虑最右侧的表达式。

例如,以下代码
1
a = (b=3, b+2);

将首先把值3赋给b,然后把b+2赋给变量a。所以,最后,变量a将包含值5,而变量b将包含值3。

位运算符(&、|、^、~、<<、>>)

位运算符通过考虑表示其存储值的位模式来修改变量。

运算符汇编等效描述
&与 (AND)按位与
|或 (OR)按位包含或
^异或按位异或
~非 (NOT)一元补码(位反转)
<<SHL左移位
>>SHR右移位

显式类型转换运算符

类型转换运算符允许将一种类型的值转换为另一种类型。在C++中有多种方法可以做到这一点。最简单的一种,是从C语言继承而来的,是使用括号(())括起来的新类型作为要转换的表达式的前缀。

1
2
3
int i;
float f = 3.14;
i = (int) f;

前面的代码将浮点数3.14转换为整数值(3);小数部分被丢弃。这里,类型转换运算符是(int)。在C++中执行相同操作的另一种方法是使用函数式表示法,将要转换的类型作为表达式的前缀,并将表达式括在括号中。

1
i = int (f);

这两种类型转换方法在C++中都是有效的。

sizeof

此运算符接受一个参数,该参数可以是类型或变量,并返回该类型或对象的字节大小。

1
x = sizeof (char);

这里,x被赋为值1,因为char是一种大小为一字节的类型。

sizeof返回的值是一个编译时常量,因此它总是在程序执行之前确定的。

其他运算符

在后续的教程中,我们将看到更多运算符,例如指向指针的运算符或面向对象编程的特定运算符。

运算符优先级

一个表达式可能包含多个运算符。例如:

1
x = 5 + 7 % 2;

在C++中,上面的表达式总是将6赋给变量x,因为%运算符的优先级高于+运算符,并且总是先进行计算。表达式的部分可以通过括号括起来来覆盖此优先级顺序,或使其意图明确。请注意区别:

1
2
x = 5 + (7 % 2);    // x = 6 (same as without parenthesis)
x = (5 + 7) % 2;    // x = 0 

从最高到最低优先级,C++运算符的计算顺序如下:
级别优先级组运算符描述分组
1作用域::作用域限定符从左到右
2后缀(一元)++ --后缀自增/自减从左到右
()函数形式
[]下标
. ->成员访问
3前缀(一元)++ --前缀自增/自减从右到左
~ !按位非/逻辑非
+ -一元前缀
& *引用/解引用
new delete分配/释放
sizeof参数包
(类型)C风格类型转换
4指针到成员.* ->*访问指针从左到右
5算术:缩放* / %乘法、除法、取模从左到右
6算术:加法+ -加法、减法从左到右
7位移<< >>左移、右移从左到右
8关系< > <= >=比较运算符从左到右
9相等== !=相等/不等从左到右
10&按位与从左到右
11异或^按位异或从左到右
12|按位或从左到右
13合取&&逻辑与从左到右
14析取||逻辑或从左到右
15赋值级表达式= *= /= %= += -=
>>= <<= &= ^= |=
赋值/复合赋值从右到左
?:条件运算符
16排序,逗号分隔符从左到右

当表达式有两个相同优先级的运算符时,分组决定了哪个运算符先被求值:是从左到右还是从右到左。

将所有子语句括在括号中(即使是由于优先级而不需要的)可以提高代码的可读性。
Index
目录