[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 | , | 逗号运算符 | 表达式, 表达式,… | 左到右 | -- |