[Relearning C/Cpp] Operators and Expressions
1 算数运算符
运算符 | 描述 | 实例 |
---|---|---|
+ | 把两个操作数相加 | A + B 将得到 30 |
- | 从第一个操作数中减去第二个操作数 | A - B 将得到 -10 |
* | 把两个操作数相乘 | A * B 将得到 200 |
/ | 分子除以分母 | B / A 将得到 2 |
% | 取模运算符,整除后的余数 | B % A 将得到 0 |
++ | 自增运算符,整数值增加 1 | A++ 将得到 11 |
– | 自减运算符,整数值减少 1 | A– 将得到 9 |
没啥好说的,可能比较有意思的是,++
和--
了
1 | int main() { |
第一眼看过去,那不得炸毛?啥,不说等于10,起码也得等于9吧。说好的先运算,再自加1呢。其实感觉都被那句话给带偏了,前置++是先自增,再运算;后置++是先运算,再自增。这句话是不准确的,准确来说,无论前置++还是后置++,都是先+1,再运算,区别是后置++会先将原来的值存在一个临时变量中,运算的时候用这个临时变量运算。
在第五版的C++ Primer的132页,有这么一句话:前置版本的递增运算符避免了不必要的工作,它把值加1后直接返回改变了的运算对象。与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容。也就是说,真实情况应该是这样的:
1 | int main() { |
不信?我们看看汇编代码(gcc -S main.cpp -o main.s
):
1 | ; 略... |
所以C++ Primer中推荐优先使用前置运算符
2 逻辑和关系运算符
运算符 | 描述 | 实例 |
---|---|---|
&& | 称为逻辑与运算符。如果两个操作数都非零,则条件为真。 | (A && B) 为假。 |
|| | 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 | (A || B) 为真。 |
! | 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。 | |
== | 检查两个操作数的值是否相等,如果相等则条件为真。 | (A == B) 不为真。 |
!= | 检查两个操作数的值是否相等,如果不相等则条件为真。 | (A != B) 为真。 |
> | 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 | (A > B) 不为真。 |
< | 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 | (A < B) 为真。 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 | (A >= B) 不为真。 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 | (A <= B) 为真。 |
注意&&和||是短路的。
1 | (expr1) && (expr2); // 如果计算出expr1是false,就不会再去计算expr2 |
3 赋值运算符
任何一种复合赋值运算符都等价于a = a op b
。例如:a += 1;
就等价于a = a + 1;
。唯一的区别就是,使用复合赋值运算符只求值一次,使用普通赋值运算需要求值两次。第一次,右边运算求一次值;第二次,赋值的时候左侧运算对象求值。这么说来还是复合赋值运算符好一点
4 位运算符
假设变量 A 的值为 60,变量 B 的值为 13,则:
运算符 | 描述 | 实例 |
---|---|---|
& | 如果同时存在于两个操作数中,二进制 AND 运算符复制一位到结果中。 | (A & B) 将得到 12,即为 0000 1100 |
| | 如果存在于任一操作数中,二进制 OR 运算符复制一位到结果中。 | (A | B) 将得到 61,即为 0011 1101 |
^ | 如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中。 | (A ^ B) 将得到 49,即为 0011 0001 |
~ | 二进制补码运算符是一元运算符,具有”翻转”位效果,即0变成1,1变成0。 | (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。 |
<< | 二进制左移运算符。左操作数的值向左移动右操作数指定的位数。 | A << 2 将得到 240,即为 1111 0000 |
>> | 二进制右移运算符。左操作数的值向右移动右操作数指定的位数。 | A >> 2 将得到 15,即为 0000 1111 |
位运算符作用于位,并逐位执行操作。在kernel中经常喜欢用位运算做flag判断,感受一下:
1 | static unsigned FLAG_0 = 0b000; // 0 |
强烈建议位运算只用于无符号数..
5 杂项
条件运算符:?:
,唯一一个三元运算符,就是一个简化版的if-else,但是效率比if-else高
sizeof运算符:sizeof(expr)
,事实上并不会去使用expr,所以即使sizeof(*ptr)
,其中ptr是null,也不会报错,多用于求数组长度,毕竟C/C++没有arr.length
的用法
逗号运算符:v = expr1, expr2
,从左向右的顺序依次求值,然后丢弃左侧结果的值,真正返回的是右侧的值
6 成员访问运算符
就是点运算符.
和箭头运算符->
。其中点运算符是获取类对象的一个成员。ptr->mem
或(*ptr).mem
,注意解引用运算符低于点运算符
运算符优先级:https://blog.csdn.net/yuliying/article/details/72898132
优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
1 | [] | 数组下标 | 数组名[常量表达式] | 左到右 | -- |
() | 圆括号 | (表达式)/函数名(形参表) | -- | ||
. | 成员选择(对象) | 对象.成员名 | -- | ||
-> | 成员选择(指针) | 对象指针->成员名 | -- | ||
| |||||
2 | - | 负号运算符 | -表达式 | 右到左 | 单目运算符 |
~ | 按位取反运算符 | ~表达式 | |||
++ | 自增运算符 | ++变量名/变量名++ | |||
-- | 自减运算符 | --变量名/变量名-- | |||
* | 取值运算符 | *指针变量 | |||
& | 取地址运算符 | &变量名 | |||
! | 逻辑非运算符 | !表达式 | |||
(类型) | 强制类型转换 | (数据类型)表达式 | -- | ||
sizeof | 长度运算符 | sizeof(表达式) | -- | ||
| |||||
3 | / | 除 | 表达式/表达式 | 左到右 | 双目运算符 |
* | 乘 | 表达式*表达式 | |||
% | 余数(取模) | 整型表达式%整型表达式 | |||
4 | + | 加 | 表达式+表达式 | 左到右 | 双目运算符 |
- | 减 | 表达式-表达式 | |||
5 | << | 左移 | 变量<<表达式 | 左到右 | 双目运算符 |
>> | 右移 | 变量>>表达式 | |||
| |||||
6 | > | 大于 | 表达式>表达式 | 左到右 | 双目运算符 |
>= | 大于等于 | 表达式>=表达式 | |||
< | 小于 | 表达式<表达式 | |||
<= | 小于等于 | 表达式<=表达式 | |||
7 | == | 等于 | 表达式==表达式 | 左到右 | 双目运算符 |
!= | 不等于 | 表达式!= 表达式 | |||
| |||||
8 | & | 按位与 | 表达式&表达式 | 左到右 | 双目运算符 |
9 | ^ | 按位异或 | 表达式^表达式 | 左到右 | 双目运算符 |
10 | | | 按位或 | 表达式|表达式 | 左到右 | 双目运算符 |
11 | && | 逻辑与 | 表达式&&表达式 | 左到右 | 双目运算符 |
12 | || | 逻辑或 | 表达式||表达式 | 左到右 | 双目运算符 |
| |||||
13 | ?: | 条件运算符 | 表达式1? 表达式2: 表达式3 | 右到左 | 三目运算符 |
| |||||
14 | = | 赋值运算符 | 变量=表达式 | 右到左 | -- |
/= | 除后赋值 | 变量/=表达式 | -- | ||
*= | 乘后赋值 | 变量*=表达式 | -- | ||
%= | 取模后赋值 | 变量%=表达式 | -- | ||
+= | 加后赋值 | 变量+=表达式 | -- | ||
-= | 减后赋值 | 变量-=表达式 | -- | ||
<<= | 左移后赋值 | 变量<<=表达式 | -- | ||
>>= | 右移后赋值 | 变量>>=表达式 | -- | ||
&= | 按位与后赋值 | 变量&=表达式 | -- | ||
^= | 按位异或后赋值 | 变量^=表达式 | -- | ||
|= | 按位或后赋值 | 变量|=表达式 | -- | ||
| |||||
15 | , | 逗号运算符 | 表达式,表达式,… | 左到右 | -- |