运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C++内置了丰富的运算符,并提供了以下类型的运算符:
- 算数运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 杂项运算符
在程序中,运算符是用来操作数据的,因此,这些数据也被称作操作数。使用运算符将操作数连接而成的式子称为:表达式。 表达式的特点:
- 变量和常量都可以认为是表达式;
- 运算符的类型对应了表达式的类型,如算术运算符对应算数表达式;
- 每一个表达式都有自己的值,即表达式都有运算结果;
算数运算符
A=10,B=20
运算符 | 描述 | 实例 |
---|---|---|
+ | 两个操作数相加 | A+B=30 |
- | 第一个操作数减去第二个操作数 | A-B=-10 |
* | 两个操作数相乘 | A*B=200 |
/ | 分子除以分母 | B/A=2 |
% | 取模运算符,整除后的余数 | B%A=0 |
++ | 自增运算符,整数值增加1 | ++A = 11 |
-- | 自减运算符,整数值减少1 | - -A=9 |
除法运算中整数和浮点数的运算结果不太一样,整数除的话结果就是整数,要想输出完整整型数:需要用浮点数去除:
1 | int A = 10; |
自增/自减运算符放在变量的前面和后面是不一样的。放在变量前面称为前缀运算,先取变量的地址,做运算后再把值放入寄存器(先变后用);放在变量后面称为后缀运算,先取变量的地址中的内容放入寄存器,然后对内存中的信息做运算(先用后变)。因为前缀运算返回的是操作数本身,所以前缀运算可以作为左值表达式;后缀运算返回的是临时变量,只能用于后缀表达式。
自增和自减运算符只能用于变量,不能用于表达式:6++、(i+j)++、(&p)++这些都是不合法的。
C/C++编译器对程序编译时,从左到右尽可能多的将字符组成一个运算符或者标识符,因此"i+++j++"是合法的,因为编译器会理解为"(i++)+(j++)",这两个自增运算符作用的对象都是变量;但是"++i+++j"是不合法的,编译器会理解为"++(i++)+j",其中第一个自增运算符作用的对象是"i++",这是一个表达式,不合法。
1 | int A = 10; |
关系运算符
A=10 ;B=20
运算符 | 描述 | 实例 |
---|---|---|
== | 检查两个操作数是否相等,如果相等则条件为真 | (A == B)不为真 |
!= | 检查两个操作数是否相等,如果不相等则条件为真 | (A!=B)为真 |
> | 检查左操作数是否大于右操作数,如果是则条件为真 | (A>B)不为真 |
< | 检查左操作数是否小于右操作数,如果是则条件为真 | (A<B)为真 |
>= | 检擦左操作数是否大于等于右操作数,如果是则为真 | (A>=B)不为真 |
<= | 检查左操作数是否小于等于右操作数,如果是则条件为真 | (A<=b)为真 |
关系运算符返回的都是bool类型的值。千万不要连起来用:"i< j < k"这种写法有问题,"i < j"运算完成后直接返回true或false,是拿这个true或false来和k做比较的。
逻辑运算符
A = true; B = false;
运算符 | 描述 | 实例 |
---|---|---|
&& | 逻辑与运算符。如果两个操作数不为零,则条件为真 | (A && B)为假 |
|| | 逻辑或运算。两个操作数任意一个非零,则条件为真 | (A || B)为真 |
! | 逻辑非运算,用来逆转操作数的逻辑状态,是单目运算符 | !(A && B)为真 |
可以用逻辑运算符来表示德摩根律:\(\neg(A \land B)\) 等价于 \(\neg A \lor \neg B\); \(\neg(A \lor B)\) 等价于 \(\neg A \land \neg B\)
1 | cout << (!(A || B) == (!A && !B)) << endl; |
逻辑运算符本身也是有优先级的 1
2auto a = true || true && false; //true
auto b = (true || true) && false; //false
逻辑与和逻辑或求值策略都是“短路求值”:先计算其左边的操作数,只有再仅靠左操作数无法确定逻辑表达式的值的时候,才会求解右操作数。
赋值运算符
把右侧的值赋给左侧。注意左侧的值一定要是一个变量。
运算符 | 描述 |
---|---|
= | 赋值运算 |
+= | 加且赋值运算符 |
-= | 减且赋值运算符 |
*= | 乘且赋值运算符 |
/= | 除且等于运算符 |
%= | 求模且等于运算符 |
<<= | 左移且赋值运算符 |
>>= | 右移且赋值运算符 |
&= | 按位与且赋值运算符 |
^= | 按位异或且赋值运算符 |
|= | 按位或且赋值运算符 |
位运算符
位运算符作用于位(bit),并逐位进行操作。
p | q | p&q | p | q | p^q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
位运算符还包括取反操作"~"和移位运算"<<"和">>",都是以bit为单位运算。
与、或、异或都是双目运算符,结合性从左到右,优先级高于逻辑运算符,低于关系运算符,且从高到低为&、^、|
使用移位运算符需要注意,左移运算还比较简单,移走的位自动填充0,但右移运算有两种情况,逻辑右移时移走的位填充0,但算数右移时移走的位与符号位有关。底层到底是逻辑右移还是算数右移取决于编译器,而不是程序员,所以对于有符号数,尽可能不要使用右移运算!!!
1 | // 位运算 |
异或操作有特性:两个相同的数异或后结果为0,且满足交换律,即"A ^ B ^ C ^ D ^ F ^ B"等价于"A ^ C ^ D ^ E ^ F"。这个特性常用来寻找成对出现的数据时缺失的哪一个数:如对于一组数【A、B、C、D、A、B、C】,直接做异或运算"ABCDABC=D"找出缺失的D。
异或操作还可以交换两个变量的值,利用了异或操作的自反性(a ^ a = 0)、恒等性(a ^ 0 = a)以及交换律(a ^ b = b ^ a、(a ^ b) ^ c = a ^ (b ^ c))。不过要注意两个变量不能相等,相等后结果为0:
1 | a = a ^ b; //步骤1 |
杂项运算符
运算符 | 描述 |
---|---|
sizeof | sizeof运算符,返回变量的大小,即这个变量的类型所占用的byte的长度 |
Condition ?X:Y | 条件运算符,唯一的三目运算符。如果Condition为真,则值为X,否则值为Y |
, | 逗号运算符,会执行一系列运算,整个逗号运算符表达式的值是以逗号分隔的列表中的最后一个表达式的值 |
.(点)和->(箭头) | 成员运算符,用于引用类、结构体和共用体的成员 |
Cast | 强制类型转换运算符,把一种数据类型转换成另一种数据类型,如int(2.20)将返回2。C++中不建议使用强制转换 |
& | 指针运算符&,返回变量的地址 |
* | 指针运算符*,指向一个变量 |
一定要注意,sizeof是运算符,不要理解为函数!!!sizeof的操作数可以是一个表达式或者是类型名。需要牢记sizeof的计算发生在编译时期,所以它可以被当作常量表达式使用,且会忽略括号内的运算。比如sizeof(a++)中的++不会执行。
如果sizeof中传递的是个函数调用,则返回函数返回类型的大小,函数不会调用。这里只能传递函数调用,不能传递函数名(Func()这个是函数调用,Func是函数名)。且如果函数返回的是void,不能确定类型,也不可以使用sizeof运算符。
运算符优先级
下表从高到低列出各个运算符,较高的运算符会被优先计算:
类别 | 运算符 | 结合性 |
---|---|---|
后缀 | () [] ++ -- | 从左到右 |
一元 | + - ! ~ ++ -- (type)* & sizeof() | 从右到左 |
乘除 | * / % | 从左到右 |
加减 | + - | 从左到右 |
移位 | << >> | 从左到右 |
关系 | < <= > >= | 从左到右 |
相等 | == != | 从左到右 |
位与AND | & | 从左到右 |
位异或XOR | ^ | 从左到右 |
位或OR | | | 从左到右 |
逻辑与 | && | 从左到右 |
逻辑或 | || | 从左到右 |
条件 | ?: | 从右到左 |
赋值 | = += -= *= = %= >>= <<= ^= |= | 从右到左 |
逗号 | , | 从左到右 |
只需要记住两点:
- 一般来说,一元运算符优先级高于对应的二元运算符;
- 弄不清就加括号