跳转至

运算

算术运算符

运算符功能
+ (单目)
- (单目)
* (双目)乘法
/除法
%取模
+ (双目)加法
- (双目)减法
单目与双目运算符

单目运算符(又称一元运算符)指被操作对象只有一个的运算符,而双目运算符(又称二元运算符)的被操作对象有两个.例如 1 + 2 中加号就是双目运算符,它有 12 两个被操作数.此外 C++ 中还有唯一的一个三目运算符 ?:

算术运算符中有两个单目运算符(正、负)以及五个双目运算符(乘法、除法、取模、加法、减法),其中单目运算符的优先级最高.

其中取模运算符 % 意为计算两个整数相除得到的余数,即求余数.

- 为双目运算符时做减法运算符,如 2-1 ;为单目运算符时做负值运算符,如 -1

使用方法如下

op=x-y*z

得到的 op 的运算值遵循数学中加减乘除的优先规律,首先进行优先级高的运算,同优先级按运算的结合性运算,括号提高优先级.

算术运算中的类型转换

对于双目算术运算符,当参与运算的两个变量类型相同时,不发生 类型转换,运算结果将会用参与运算的变量的类型容纳,否则会发生类型转换,以使两个变量的类型一致.转换的规则参见 类型转换

例如,对于一个整型(int)变量 x 和另一个双精度浮点型(double)类型变量 y

  • x/3 的结果将会是整型;
  • x/3.0 的结果将会是双精度浮点型;
  • x/y 的结果将会是双精度浮点型;
  • x*1/3 的结果将会是整型;
  • x*1.0/3 的结果将会是双精度浮点型;

位操作符

另请参阅:位运算

运算符功能
~逐位非
& (双目)逐位与
|逐位或
^逐位异或
<<逐位左移
>>逐位右移

位操作的意义请参考 位操作 页面.需要注意的是,位操作的优先级低于算术运算符(除了取反),而按位与、按位或及异或低于比较运算符(详见 C++ 运算符优先级总表),所以使用时需多加注意,在必要时添加括号.

移位运算中如果出现如下情况,则其行为未定义:

  1. 右操作数(即移位数)为负值;
  2. 右操作数大于等于左操作数的位数;

例如,对于 int32_t 类型的变量 aa<<-1a<<32 都是未定义的.

对于带符号非负数的左移操作,需要确保移位后的结果能被原数的类型容纳,否则行为也是未定义的.1对一个负数执行左移操作也未定义.2

对于右移操作,右侧多余的位将会被舍弃,而左侧较为复杂:对于无符号数,会在左侧补 03;而对于有符号数,则会用最高位的数(其实就是符号位,非负数为 0,负数为 1)补齐4

自增/自减 运算符

有时我们需要让变量进行增加 1(自增)或者减少 1(自减),这时自增运算符 ++ 和自减运算符 -- 就派上用场了.

自增/自减运算符可放在变量前或变量后面,在变量前称为前缀,在变量后称为后缀,单独使用时前缀后缀无需特别区别,如果需要用到表达式的值则需注意,具体可看下面的例子.详细情况可参考 引用 介绍的例子部分.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
i = 100;

op1 = i++;  // op1 = 100,先 op1 = i,然后 i = i + 1

i = 100;

op2 = ++i;  // op2 = 101,先 i = i + 1,然后赋值 op2

i = 100;

op3 = i--;  // op3 = 100,先赋值 op3,然后 i = i - 1

i = 100;

op4 = --i;  // op4 = 99,先 i = i - 1,然后赋值 op4

复合赋值运算符

复合赋值运算符实际上是表达式的缩写形式.可分为复合算术运算符 +=-=*=/=%= 和复合位操作符 &=|=^=<<=>>=

例如,op = op + 2 可写为 op += 2op = op - 2 可写为 op -= 2op= op * 2 可写为 op *= 2

条件运算符

条件运算符可以看作 if 语句的简写,a ? b : c 中如果表达式 a 成立,那么这个条件表达式的结果是 b,否则条件表达式的结果是 c

比较运算符

运算符功能
>大于
>=大于等于
<小于
<=小于等于
==等于
!=不等于

其中特别需要注意的是要将等于运算符 == 和赋值运算符 = 区分开来,这在判断语句中尤为重要.

if (op=1)if (op==1) 看起来类似,但实际功能却相差甚远.第一条语句是在对 op 进行赋值,若赋值为非 0 时为真值,表达式的条件始终是满足的,无法达到判断的作用;而第二条语句才是对 op 的值进行判断.

逻辑运算符

运算符功能
&&逻辑与
||逻辑或
!逻辑非

1
2
3
4
5
Result = op1 && op2;  // 当 op1 与 op2 都为真时则 Result 为真

Result = op1 || op2;  // 当 op1 或 op2 其中一个为真时则 Result 为真

Result = !op1;  // 当 op1 为假时则 Result 为真

内建的运算符 &&|| 进行短路求值(若在求值第一个操作数后结果已知,则不求值第二个),重载的运算符无此特性,并始终对两个操作数都进行求值.

逗号运算符

逗号运算符可将多个表达式分隔开来,被分隔开的表达式按从左至右的顺序依次计算,整个表达式的值是最后的表达式的值.逗号表达式的优先级在所有运算符中的优先级是 最低 的.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
exp1, exp2, exp3;  // 最后的值为 exp3 的运算结果.

Result = 1 + 2, 3 + 4, 5 + 6;
//得到 Result 的值为 3 而不是 11,因为赋值运算符 "="
//的优先级比逗号运算符高,先进行了赋值运算才进行逗号运算.

Result = (1 + 2, 3 + 4, 5 + 6);

// 若要让 Result 的值得到逗号运算的结果则应将整个表达式用括号提高优先级,此时
// Result 的值才为 11.

成员访问运算符

运算符功能
[]数组下标
.对象成员
& (单目)取地址/获取引用
* (单目)间接寻址/解引用
->指针成员

这些运算符用来访问对象的成员或者内存,除了最后一个运算符外上述运算符都可被重载.与 &*-> 相关的内容请阅读 指针引用 教程.这里还省略了两个很少用到的运算符 .*->* ,其具体用法可以参见 C++ 语言手册

1
2
3
4
5
auto result1 = v[1];  // 获取v中下标为2的对象
auto result2 = p.q;   // 获取p对象的q成员
auto result3 = p -> q;  // 获取p指针指向的对象的q成员,等价于 (*p).q
auto result4 = &v;      // 获取指向v的指针
auto result5 = *v;      // 获取v指针指向的对象

C++ 运算符优先级总表

来自 C++ 运算符优先级 - cppreference ,有修改.

运算符描述例子可重载性
第一级别
::作用域解析符Class::age = 2;不可重载
第二级别
++后自增运算符for (int i = 0; i < 10; i++) cout << i;可重载
--后自减运算符for (int i = 10; i > 0; i--) cout << i;可重载
type() type{}强制类型转换unsigned int a = unsigned(3.14);可重载
()函数调用isdigit('1')可重载
[]数组数据获取array[4] = 2;可重载
.对象型成员调用obj.age = 34;不可重载
->指针型成员调用ptr->age = 34;可重载
第三级别 (从右向左结合)
++前自增运算符for (i = 0; i < 10; ++i) cout << i;可重载
--前自减运算符for (i = 10; i > 0; --i) cout << i;可重载
+正号int i = +1;可重载
-负号int i = -1;可重载
!逻辑取反if (!done) …可重载
~按位取反flags = ~flags;可重载
(type)C 风格强制类型转换int i = (int) floatNum;可重载
*指针取值int data = *intPtr;可重载
&值取指针int *intPtr = &data;可重载
sizeof返回类型内存int size = sizeof floatNum; int size = sizeof(float);不可重载
new动态元素内存分配long *pVar = new long; MyClass *ptr = new MyClass(args);可重载
new []动态数组内存分配long *array = new long[n];可重载
delete动态析构元素内存delete pVar;可重载
delete []动态析构数组内存delete [] array;可重载
第四级别
.*类对象成员引用obj.*var = 24;不可重载
->*类指针成员引用ptr->*var = 24;可重载
第五级别
*乘法int i = 2 * 4;可重载
/除法float f = 10.0 / 3.0;可重载
%取余数(模运算)int rem = 4 % 3;可重载
第六级别
+加法int i = 2 + 3;可重载
-减法int i = 5 - 1;可重载
第七级别
<<位左移int flags = 33 << 1;可重载
>>位右移int flags = 33 >> 1;可重载
第八级别
<=>三路比较运算符if ((i <=> 42) < 0) ...可重载
第九级别
<小于if (i < 42) ...可重载
<=小于等于if (i <= 42) ...可重载
>大于if (i > 42) ...可重载
>=大于等于if (i >= 42) ...可重载
第十级别
==等于if (i == 42) ...可重载
!=不等于if (i != 42) ...可重载
第十一级别
&位与运算flags = flags & 42;可重载
第十二级别
^位异或运算flags = flags ^ 42;可重载
第十三级别
|位或运算flags = flags | 42;可重载
第十四级别
&&逻辑与运算if (conditionA && conditionB) ...可重载
第十五级别
||逻辑或运算if (conditionA || conditionB) ...可重载
第十六级别 (从右向左结合)
? :条件运算符int i = a > b ? a : b;不可重载
throw异常抛出throw EClass("Message");不可重载
=赋值int a = b;可重载
+=加赋值运算a += 3;可重载
-=减赋值运算b -= 4;可重载
*=乘赋值运算a *= 5;可重载
/=除赋值运算a /= 2;可重载
%=模赋值运算a %= 3;可重载
<<=位左移赋值运算flags <<= 2;可重载
>>=位右移赋值运算flags >>= 2;可重载
&=位与赋值运算flags &= new_flags;可重载
^=位异或赋值运算flags ^= new_flags;可重载
|=位或赋值运算flags |= new_flags;可重载
第十七级别
,逗号分隔符for (i = 0, j = 0; i < 10; i++, j++) ...可重载

需要注意的是,表中并未列出 const_caststatic_castdynamic_castreinterpret_casttypeidsizeof...noexceptalignof 等运算符,因为它们的使用形式与函数调用相同,不会出现歧义.

参考资料与注释


  1. C++20 前,若原值为带符号类型,且移位后的结果能被原类型的无符号版本容纳,则将该结果 转换 为相应的带符号值,否则行为未定义;无符号数的左移则舍弃移出结果类型的位.C++20 起,规定 a << ba2b 在模 2N 下的值(N 为结果类型的位宽),即无论是带符号数还是无符号数,左移均直接舍弃移出结果类型的位(即 算术左移/逻辑左移). 

  2. C++20 前.C++20 起的行为参见1. 

  3. 逻辑右移. 

  4. 算术右移.C++20 前,带符号的右移是依实现定义的,在大多数实现中,均采用算术右移.C++20 起,规定 a >> ba/2b,所以带符号数右移运算是算术右移.