运算符
一旦我们了解了变量和常量的存在,我们就可以开始对它们进行操作。为此,C++集成了运算符。与其他主要使用关键字作为运算符的语言不同,C++中的运算符主要由符号组成,这些符号不是字母表的一部分,但在所有键盘上都可用。这使得C++代码更简洁,更国际化,因为它较少依赖于英语单词,但需要一开始付出一些学习努力。
您不必记住此页面的所有内容。提供大部分细节只是为了在您需要时作为以后的参考。
赋值 (=)
赋值运算符将一个值赋给一个变量。
此语句将整数值 5 赋给变量
a。赋值运算符左边的部分(
=)被称为
lvalue(左值),右边的部分被称为
rvalue(右值)。lvalue 必须是一个变量,而 rvalue 可以是一个常量、一个变量、一个操作的结果或这些的任意组合。
赋值时最重要的规则是
从右到左的规则:赋值操作总是从右到左进行,永远不会反过来。
这条语句将变量
a(左值)赋值为变量
和 b(右值)中包含的值。 存储到此刻的值
a在此操作中根本不被考虑,事实上,该值会丢失。
还要考虑到,我们只是在赋值操作的那一刻赋值了
和 b为
a的值。因此,之后对
和 b的更改不会影响
a.
的新值。例如,让我们看一下以下代码——我已经将变量中存储内容的演变作为注释包含在内
|
// 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;
return 0;
}
|
a:4 b:7 |
此代码将给我们结果,
a的 C++ 等效文件是
4和
和 b的 C++ 等效文件是
7中包含的值是相等的。请注意
a并没有受到
和 b的最终修改的影响,即使我们之前声明了
a = b(这是因为
从右到左的规则)。
C++相对于其他编程语言的一个特性是,赋值操作可以用作另一个赋值操作的右值(或右值的一部分)。例如
等价于
这意味着:首先将
5赋值给变量
和 b,然后将
a的值加上之前赋值
2的结果(即 5)赋值给
和 b,使
a的最终值为
7.
以下表达式在 C++ 中也是有效的
它将
5赋值给所有三个变量
a,
和 b和
c.
算术运算符 (+, -, *, /, %)
C++ 语言支持的五种算术运算是
+ | 加法 (addition) |
- | subtraction (减法) |
* | 乘法 |
/ | 除法 |
% | 取模 |
加法、减法、乘法和除法的运算与它们各自的数学运算符完全对应。你可能不那么熟悉的唯一一个是
取模;它的运算符是百分号(
%)。取模运算给出两个值相除的余数。例如,如果我们写
变量
a将包含值
2,因为
2是将
11除以
3.
的余数。
当我们想要通过对变量中当前存储的值执行操作来修改变量的值时,我们可以使用复合赋值运算符。
表达式 | 等价于 |
value += increase; | value = value + increase; |
a -= 5; | a = a - 5; |
a /= b; | a = a / b; |
price *= units + 1; | price = price * (units + 1); |
与其他所有运算符相同。例如
1 2 3 4 5 6 7 8 9 10 11 12 13
|
// 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;
return 0;
}
|
5 |
递增和递减 (++, --)
为了进一步缩短某些表达式,递增运算符 (
++) 和递减运算符 (
--) 将变量中存储的值增加或减少 1。它们分别等同于
+=1和
-=1。因此
在功能上都是等效的:它们三个都将 c 的值增加 1。
在早期的 C 编译器中,根据使用的是哪个表达式,这三个表达式可能会产生不同的可执行代码。 如今,这种类型的代码优化通常由编译器自动完成,因此这三个表达式应产生完全相同的可执行代码。
此运算符的一个特点是,它既可以用作前缀,也可以用作后缀。这意味着它可以写在变量标识符之前(
++a),也可以写在它之后(
a++)。虽然在简单的表达式中,例如
a++或
++a,两者具有完全相同的含义,但在递增或递减运算的结果作为外部表达式中的值进行求值的其他表达式中,它们可能在其含义上存在重要差异:如果递增运算符用作前缀 (++a),则该值在表达式结果求值
之前递增,因此在外部表达式中会考虑递增后的值;如果递增运算符用作后缀(
a++),则存储在 a 中的值在求值后递增,因此在递增运算之前存储的值会在外部表达式中求值。注意区别
示例 1 | 示例 2 |
B=3; A=++B; // A 包含 4,B 包含 4 |
B=3; A=B++; // A 包含 3,B 包含 4 |
在示例 1 中,
B的值在复制到
A之前递增。 而在示例 2 中,
B的值被复制到
A,然后
B递增。
关系运算符和相等运算符 ( ==, !=, >, <, >=, <= )
为了评估两个表达式之间的比较,我们可以使用关系运算符和相等运算符。关系运算的结果是一个布尔值,根据其布尔结果,该布尔值只能为真或假。
我们可能想比较两个表达式,例如,了解它们是否相等,或者一个是否大于另一个。以下是可以用于 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=2,
b=3和
c=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进行比较,
逻辑运算符 ( !, &&, || )
运算符
!是 C++ 运算符,用于执行布尔运算 NOT,它只有一个操作数,位于其右侧,它所做的唯一一件事就是反转该操作数的值,如果其操作数为真,则产生假,如果其操作数为假,则产生真。 基本上,它返回评估其操作数的相反布尔值。 例如
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。 如果其两个操作数都为真,则此运算结果为真,否则为假。 下面的面板显示了运算符
&&评估表达式
a && b:
&& 运算符
a | 和 b | a && b |
true | true | true |
true | false | false |
false | true | false |
false | false | false |
运算符
||对应于布尔逻辑运算 OR。 如果其两个操作数中的任何一个为真,则此运算结果为真,因此只有当两个操作数本身都为假时才为假。 这是
a || b:
的可能结果
a | 和 b | a || b |
true | true | true |
true | false | true |
false | true | true |
false | false | false |
例如:
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是否为真,如果是,它将永远不会检查
3>6是否为真。 这被称为短路求值,对于这些运算符,它是这样工作的
运算符 | 短路 |
&& | 如果左侧表达式为假,则组合结果为假(不评估右侧表达式)。 |
|| | 如果左侧表达式为真,则组合结果为真(不评估右侧表达式)。 |
当右侧表达式具有副作用(例如更改值)时,这最为重要。
1
|
if ((i<10)&&(++i<n)) { /*...*/ }
|
这个组合的条件表达式递增
i的值 1,但仅当
&&左侧的条件为真时,否则右侧表达式 (
++i<n) 永远不会被评估。
条件运算符 ( ? )
条件运算符评估一个表达式,如果该表达式为真,则返回一个值;如果该表达式被评估为假,则返回另一个值。其格式为
condition ? result1 : result2
如果已知
condition为真,表达式将返回
result1,如果为假,则返回
result2.
1 2 3 4
|
7==5 ? 4 : 3 // returns 3, since 7 is not equal to 5.
7==5+2 ? 4 : 3 // returns 4, since 7 is equal to 5+2.
5>3 ? a : b // returns the value of a, since 5 is greater than 3.
a>b ? a : b // returns whichever is greater, a or b.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
// conditional operator
#include <iostream>
using namespace std;
int main ()
{
int a,b,c;
a=2;
b=7;
c = (a>b) ? a : b;
cout << c;
return 0;
}
|
7 |
在这个例子中
a是
2和
和 b是
7,所以被评估的表达式 (
a>b) 不为真,因此问号后指定的第一个值被丢弃,取而代之的是第二个值(冒号后的那个值),该值是
和 b,其值为
7.
逗号运算符 ( , )
逗号运算符 (
,) 用于分隔预期只有一个表达式的位置中包含的两个或多个表达式。当必须评估表达式集的值时,仅考虑最右边的表达式。
例如,以下代码
将首先赋值
3为
和 b,然后赋值
b+2赋值给变量
a。 因此,最后,变量
a将包含值
5,而变量
和 b将包含值
3.
按位运算符 ( &, |, ^, ~, <<, >> )
按位运算符修改变量,同时考虑表示它们存储的值的位模式。
运算符 | asm 等价物 | 描述 |
& | 与 (AND) | 按位与 |
| | 或 (OR) | 按位或(包含) |
^ | 异或 | 按位异或 |
~ | 非 (NOT) | 一元补码(位反转) |
<< | SHL | 左移 |
>> | SHR | 右移 |
显式类型转换运算符
类型转换运算符允许您将给定类型的数据转换为另一种类型。 在 C++ 中有几种方法可以做到这一点。 最简单的一种是从 C 语言继承而来,就是在要转换的表达式前加上用括号括起来的新类型 (
()):
1 2 3
|
int i;
float f = 3.14;
i = (int) f;
|
前面的代码将浮点数
3.14转换为整数值 (
3),余数丢失。 在这里,类型转换运算符是
(int)。 在 C++ 中执行相同操作的另一种方法是使用函数表示法:在要转换的表达式前加上类型,并将表达式括在括号之间
这两种类型转换方式在 C++ 中都是有效的。
sizeof()
此运算符接受一个参数,该参数可以是类型或变量本身,并返回该类型或对象的大小(以字节为单位)。
这将为 a 赋值
1,因为
char是一个单字节长的类型。
返回的值
sizeof是一个常量,因此它总是在程序执行之前确定。
其他运算符
在本教程的后面部分,我们将看到更多运算符,例如与指针相关的运算符或特定于面向对象编程的运算符。 每个运算符都在其各自的部分中进行处理。
运算符优先级
在编写具有多个操作数的复杂表达式时,我们可能会对哪个操作数先求值,哪个操作数后求值产生一些疑问。 例如,在这个表达式中
我们可能会怀疑它是否真的意味着
1 2
|
a = 5 + (7 % 2) // with a result of 6, or
a = (5 + 7) % 2 // with a result of 0
|
正确的答案是两个表达式中的第一个,结果为
6。 对于每个运算符的优先级都有一个既定的顺序,不仅是算术运算符(其偏好来自数学),而且对于可能出现在 C++ 中的所有运算符也是如此。 从最高到最低的优先级,优先级顺序如下
级别 | 运算符 | 描述 | 分组 |
1 | :: | 范围 | 从左到右 |
2 | () [] . -> ++ -- dynamic_cast static_cast reinterpret_cast const_cast typeid | 后缀 | 从左到右 |
3 | ++ -- ~ ! sizeof new delete | 一元(前缀) | 从右到左 |
* & | 间接和引用(指针) |
+ - | 一元符号运算符 |
4 | (type) | 类型转换 | 从右到左 |
5 | .* ->* | 指向成员的指针 | 从左到右 |
6 | * / % | 乘法 | 从左到右 |
7 | + - | 加法 | 从左到右 |
8 | << >> | 移位 | 从左到右 |
9 | < > <= >= | 关系 | 从左到右 |
10 | == != | 相等 | 从左到右 |
11 | & | 按位与 | 从左到右 |
12 | ^ | 按位异或 | 从左到右 |
13 | | | 按位或 | 从左到右 |
14 | && | 逻辑与 | 从左到右 |
15 | || | 逻辑或 | 从左到右 |
16 | ?: | 条件 | 从右到左 |
17 | = *= /= %= += -= >>= <<= &= ^= |= | 赋值 | 从右到左 |
18 | , | 逗号 | 从左到右 |
分组定义了在表达式中存在多个相同级别的运算符的情况下,运算符求值的优先级顺序。
所有这些运算符的优先级级别都可以通过使用括号符号来消除可能的歧义来操纵或变得更清晰
(和
),如本例所示
可以写成
或
取决于我们要执行的操作。
因此,如果您想编写复杂的表达式并且不完全确定优先级级别,请始终包含括号。 它也会使您的代码更易于阅读。