mirror of
https://github.com/Aexiar/c.git
synced 2024-10-22 12:05:45 +00:00
707 lines
23 KiB
Markdown
707 lines
23 KiB
Markdown
# 第一章:前言
|
||
|
||
## 1.1 运算符、表达式和操作数
|
||
|
||
* 运算符是一种特殊的符号,用于数据的运算、赋值和比较等。
|
||
* `表达式`指的是一组运算数、运算符的组合,表达式`一定具有值`,一个变量或一个常量可以是表达式,变量、常量和运算符也可以组成表达式,如:
|
||
|
||
![](./assets/20.svg)
|
||
|
||
* `操作数`指的是`参与运算`的`值`或者`对象`,如:
|
||
|
||
![](./assets/21.svg)
|
||
|
||
## 1.2 运算符的分类
|
||
|
||
* 根据`操作数`的`个数`,可以将运算符分为:
|
||
* 一元运算符(一目运算符)。
|
||
* 二元运算符(二目运算符)。
|
||
* 三元运算符(三目运算符)。
|
||
* 根据`功能`,可以将运算符分为:
|
||
* 算术运算符。
|
||
* 关系运算符(比较运算符)。
|
||
* 逻辑运算符。
|
||
* 赋值运算符。
|
||
* 逻辑运算符。
|
||
* 位运算符。
|
||
* 三元运算符。
|
||
|
||
|
||
> [!NOTE]
|
||
>
|
||
> 掌握一个运算符,需要关注以下几个方面:
|
||
>
|
||
> * ① 运算符的含义。
|
||
> * ② 运算符操作数的个数。
|
||
> * ③ 运算符所组成的表达式。
|
||
> * ④ 运算符有无副作用,即:运算后是否会修改操作数的值。
|
||
|
||
|
||
|
||
# 第二章:算术运算符(⭐)
|
||
|
||
## 2.1 概述
|
||
|
||
* 算术运算符是对数值类型的变量进行运算的,如下所示:
|
||
|
||
| 运算符 | 描述 | 操作数个数 | 组成的表达式的值 | 副作用 |
|
||
| ------ | ------------ | ---------- | ------------------------ | ------ |
|
||
| `+` | 正号 | 1 | 操作数本身 | ❎ |
|
||
| `-` | 负号 | 1 | 操作数符号取反 | ❎ |
|
||
| `+` | 加号 | 2 | 两个操作数之和 | ❎ |
|
||
| `-` | 减号 | 2 | 两个操作数之差 | ❎ |
|
||
| `*` | 乘号 | 2 | 两个操作数之积 | ❎ |
|
||
| `/` | 除号 | 2 | 两个操作数之商 | ❎ |
|
||
| `%` | 取模(取余) | 2 | 两个操作数相除的余数 | ❎ |
|
||
| `++` | 自增 | 1 | 操作数自增前或自增后的值 | ✅ |
|
||
| `--` | 自减 | 1 | 操作数自减前或自减后的值 | ✅ |
|
||
|
||
> [!NOTE]
|
||
>
|
||
> 自增和自减:
|
||
>
|
||
> * ① 自增、自减运算符可以写在操作数的前面也可以写在操作数后面,不论前面还是后面,对操作数的副作用是一致的。
|
||
> * ② 自增、自减运算符在前在后,对于表达式的值是不同的。 如果运算符在前,表达式的值是操作数自增、自减之后的值;如果运算符在后,表达式的值是操作数自增、自减之前的值。
|
||
> * ③ `变量前++`:变量先自增 1 ,然后再运算;`变量后++`:变量先运算,然后再自增 1 。
|
||
> * ④ `变量前--`:变量先自减 1 ,然后再运算;`变量后--`:变量先运算,然后再自减 1 。
|
||
> * ⑤ 对于 `i++` 或 `i--` ,各种编程语言的用法和支持是不同的,例如:C/C++、Java 等完全支持,Python 压根一点都不支持,Go 语言虽然支持 `i++` 或 `i--` ,却只支持这些操作符作为独立的语句,并且不能嵌入在其它的表达式中。
|
||
|
||
## 2.2 应用示例
|
||
|
||
* 示例:正号和负号
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
int main() {
|
||
|
||
int x = 12;
|
||
int x1 = -x, x2 = +x;
|
||
|
||
int y = -67;
|
||
int y1 = -y, y2 = +y;
|
||
|
||
printf("x1=%d, x2=%d \n", x1, x2); // x1=-12, x2=12
|
||
printf("y1=%d, y2=%d \n", y1, y2); // y1=67, y2=-67
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
|
||
|
||
* 示例:加、减、乘、除(整数之间做除法时,结果只保留整数部分而舍弃小数部分)、取模
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
int main() {
|
||
|
||
int a = 5;
|
||
int b = 2;
|
||
|
||
printf("%d + %d = %d\n", a, b, a + b); // 5 + 2 = 7
|
||
printf("%d - %d = %d\n", a, b, a - b); // 5 - 2 = 3
|
||
printf("%d × %d = %d\n", a, b, a * b); // 5 × 2 = 10
|
||
printf("%d / %d = %d\n", a, b, a / b); // 5 / 2 = 2
|
||
printf("%d %% %d = %d\n", a, b, a % b); // 5 % 2 = 1
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
|
||
|
||
* 示例:取模(运算结果的符号与被模数也就是第一个操作数相同。)
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
int main() {
|
||
|
||
int res1 = 10 % 3;
|
||
printf("10 %% 3 = %d\n", res1); // 10 % 3 = 1
|
||
|
||
int res2 = -10 % 3;
|
||
printf("-10 %% 3 = %d\n", res2); // -10 % 3 = -1
|
||
|
||
int res3 = 10 % -3;
|
||
printf("10 %% -3 = %d\n", res3); // 10 % -3 = 1
|
||
|
||
int res4 = -10 % -3;
|
||
printf("-10 %% -3 = %d\n", res4); // -10 % -3 = -1
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
|
||
|
||
* 示例:自增和自减
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
int main() {
|
||
|
||
int i1 = 10, i2 = 20;
|
||
int i = i1++;
|
||
printf("i = %d\n", i); // i = 10
|
||
printf("i1 = %d\n", i1); // i1 = 11
|
||
|
||
i = ++i1;
|
||
printf("i = %d\n", i); // i = 12
|
||
printf("i1 = %d\n", i1); // i1 = 12
|
||
|
||
i = i2--;
|
||
printf("i = %d\n", i); // i = 20
|
||
printf("i2 = %d\n", i2); // i2 = 19
|
||
|
||
i = --i2;
|
||
printf("i = %d\n", i); // i = 18
|
||
printf("i2 = %d\n", i2); // i2 = 18
|
||
|
||
return 0;
|
||
|
||
```
|
||
|
||
|
||
|
||
* 示例:
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
/*
|
||
随意给出一个整数,打印显示它的个位数,十位数,百位数的值。
|
||
格式如下:
|
||
数字xxx的情况如下:
|
||
个位数:
|
||
十位数:
|
||
百位数:
|
||
例如:
|
||
数字153的情况如下:
|
||
个位数:3
|
||
十位数:5
|
||
百位数:1
|
||
*/
|
||
int main() {
|
||
|
||
int num = 153;
|
||
|
||
int bai = num / 100;
|
||
int shi = num % 100 / 10;
|
||
int ge = num % 10;
|
||
printf("百位为:%d \n", bai);
|
||
printf("十位为:%d \n", shi);
|
||
printf("个位为:%d \n", ge);
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
|
||
|
||
# 第三章:关系运算符(⭐)
|
||
|
||
## 3.1 概述
|
||
|
||
* 常见的关系运算符(比较运算符),如下所示:
|
||
|
||
| 运算符 | 描述 | 操作数个数 | 组成的表达式的值 | 副作用 |
|
||
| ------ | -------- | ---------- | ---------------- | ------ |
|
||
| `==` | 相等 | 2 | 0 或 1 | ❎ |
|
||
| `!=` | 不相等 | 2 | 0 或 1 | ❎ |
|
||
| `<` | 小于 | 2 | 0 或 1 | ❎ |
|
||
| `>` | 大于 | 2 | 0 或 1 | ❎ |
|
||
| `<=` | 小于等于 | 2 | 0 或 1 | ❎ |
|
||
| `>=` | 大于等于 | 2 | 0 或 1 | ❎ |
|
||
|
||
> [!NOTE]
|
||
>
|
||
> * ① C 语言中,没有严格意义上的布尔类型,可以使用 0(假) 或 1(真)表示布尔类型的值。
|
||
> * ② 不要将 `==` 写成 `=`,`==` 是比较运算符,而 `=` 是赋值运算符。
|
||
> * ③ `>=` 或 `<=`含义是只需要满足 `大于或等于`、`小于或等于`其中一个条件,结果就返回真。
|
||
|
||
## 3.2 应用示例
|
||
|
||
* 示例:
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
int main() {
|
||
|
||
int a = 8;
|
||
int b = 7;
|
||
|
||
printf("a > b 的结果是:%d \n", a > b); // a > b 的结果是:1
|
||
printf("a >= b 的结果是:%d \n", a >= b); // a >= b 的结果是:1
|
||
printf("a < b 的结果是:%d \n", a < b); // a < b 的结果是:0
|
||
printf("a <= b 的结果是:%d \n", a <= b); // a <= b 的结果是:0
|
||
printf("a == b 的结果是:%d \n", a == b); // a == b 的结果是:0
|
||
printf("a != b 的结果是:%d \n", a != b); // a != b 的结果是:1
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
# 第四章:逻辑运算符(⭐)
|
||
|
||
## 4.1 概述
|
||
|
||
* 常见的逻辑运算符,如下所示:
|
||
|
||
| 运算符 | 描述 | 操作数个数 | 组成的表达式的值 | 副作用 |
|
||
| ------ | ------ | ---------- | ---------------- | ------ |
|
||
| `&&` | 逻辑与 | 2 | 0 或 1 | ❎ |
|
||
| `\|\|` | 逻辑或 | 2 | 0 或 1 | ❎ |
|
||
| `!` | 逻辑非 | 2 | 0 或 1 | ❎ |
|
||
|
||
* 逻辑运算符提供逻辑判断功能,用于构建更复杂的表达式,如下所示:
|
||
|
||
| a | b | a && b | a \|\| b | !a |
|
||
| ------- | ------- | ------- | -------- | ------- |
|
||
| 1(真) | 1(真) | 1(真) | 1(真) | 0(假) |
|
||
| 1(真) | 0(假) | 0(假) | 1(真) | 0(假) |
|
||
| 0(假) | 1(真) | 0(假) | 1(真) | 1(真) |
|
||
| 0(假) | 0(假) | 0(假) | 0(假) | 1(真) |
|
||
|
||
> [!NOTE]
|
||
>
|
||
> * ① 对于逻辑运算符来说,任何`非零值`都表示`真`,`零值`表示`假`,如:`5 || 0` 返回 `1` ,`5 && 0` 返回 `0` 。
|
||
> * ② 逻辑运算符的理解:
|
||
> * `&&` 的理解就是:`两边条件,同时满足`。
|
||
> * `||`的理解就是:`两边条件,二选一`。
|
||
> * `!` 的理解就是:`条件取反`。
|
||
> * ③ 短路现象:
|
||
> * 对于 `a && b` 操作来说,当 a 为假(或 0 )时,因为 `a && b` 结果必定为 0,所以不再执行表达式 b。
|
||
> * 对于 `a || b` 操作来说,当 a 为真(或非 0 )时,因为 `a || b` 结果必定为 1,所以不再执行表达式 b。
|
||
|
||
## 4.2 应用示例
|
||
|
||
* 示例:
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
int main() {
|
||
|
||
int a = 0;
|
||
int b = 0;
|
||
|
||
printf("请输入整数a的值:");
|
||
scanf("%d", &a);
|
||
printf("请输入整数b的值:");
|
||
scanf("%d", &b);
|
||
|
||
if (a > b) {
|
||
printf("%d > %d", a, b);
|
||
} else if (a < b) {
|
||
printf("%d < %d", a, b);
|
||
} else {
|
||
printf("%d = %d", a, b);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
|
||
|
||
* 示例:
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
// 短路现象
|
||
int main() {
|
||
|
||
int i = 0;
|
||
int j = 10;
|
||
if (i && j++ > 0) {
|
||
printf("床前明月光\n"); // 这行代码不会执行
|
||
} else {
|
||
printf("我叫郭德纲\n");
|
||
}
|
||
printf("%d \n", j); //10
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
|
||
|
||
* 示例:
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
// 短路现象
|
||
|
||
int main() {
|
||
|
||
int i = 1;
|
||
int j = 10;
|
||
if (i || j++ > 0) {
|
||
printf("床前明月光 \n");
|
||
} else {
|
||
printf("我叫郭德纲 \n"); // 这行代码不会被执行
|
||
}
|
||
printf("%d\n", j); //10
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
# 第五章:赋值运算符(⭐)
|
||
|
||
## 5.1 概述
|
||
|
||
* 常见的赋值运算符,如下所示:
|
||
|
||
| 运算符 | 描述 | 操作数个数 | 组成的表达式的值 | 副作用 |
|
||
| ------ | ------------ | ---------- | ---------------- | ------ |
|
||
| `==` | 赋值 | 2 | 左边操作数的值 | ✅ |
|
||
| `+=` | 相加赋值 | 2 | 左边操作数的值 | ✅ |
|
||
| `-=` | 相减赋值 | 2 | 左边操作数的值 | ✅ |
|
||
| `*=` | 相乘赋值 | 2 | 左边操作数的值 | ✅ |
|
||
| `/=` | 相除赋值 | 2 | 左边操作数的值 | ✅ |
|
||
| `%=` | 取余赋值 | 2 | 左边操作数的值 | ✅ |
|
||
| `<<=` | 左移赋值 | 2 | 左边操作数的值 | ✅ |
|
||
| `>>=` | 右移赋值 | 2 | 左边操作数的值 | ✅ |
|
||
| `&=` | 按位与赋值 | 2 | 左边操作数的值 | ✅ |
|
||
| `^=` | 按位异或赋值 | 2 | 左边操作数的值 | ✅ |
|
||
| `\|=` | 按位或赋值 | 2 | 左边操作数的值 | ✅ |
|
||
|
||
> [!NOTE]
|
||
>
|
||
> * ① 赋值运算符的第一个操作数(左值)必须是变量的形式,第二个操作数可以是任何形式的表达式。
|
||
> * ② 赋值运算符的副作用针对第一个操作数。
|
||
|
||
## 5.2 应用示例
|
||
|
||
* 示例:
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
int main() {
|
||
|
||
int a = 3;
|
||
a += 3; // a = a + 3
|
||
printf("a = %d\n", a); // a = 6
|
||
|
||
int b = 3;
|
||
b -= 3; // b = b - 3
|
||
printf("b = %d\n", b); // b = 0
|
||
|
||
int c = 3;
|
||
c *= 3; // c = c * 3
|
||
printf("c = %d\n", c); // c = 9
|
||
|
||
int d = 3;
|
||
d /= 3; // d = d / 3
|
||
printf("d = %d\n", d); // d = 1
|
||
|
||
int e = 3;
|
||
e %= 3; // e = e % 3
|
||
printf("e = %d\n", e); // e = 0
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
# 第六章:位运算符(了解)
|
||
|
||
## 6.1 概述
|
||
|
||
* C 语言提供了一些位运算符,能够让我们操作二进制位(bit)。
|
||
* 常见的位运算符,如下所示。
|
||
|
||
| 运算符 | 描述 | 操作数个数 | 运算规则 | 副作用 |
|
||
| ------ | ---------- | ---------- | ------------------------------------------------------------ | ------ |
|
||
| `&` | 按位与 | 2 | 两个二进制位都为 1 ,结果为 1 ,否则为 0 。 | ❎ |
|
||
| `\|` | 按位或 | 2 | 两个二进制位只要有一个为 1(包含两个都为 1 的情况),结果为 1 ,否则为 0 。 | ❎ |
|
||
| `^` | 按位异或 | 2 | 两个二进制位一个为 0 ,一个为 1 ,结果为 1,否则为 0 。 | ❎ |
|
||
| `~` | 按位取反 | 2 | 将每一个二进制位变成相反值,即 0 变成 1 , 1 变 成 0 。 | ❎ |
|
||
| `<<` | 二进制左移 | 2 | 将一个数的各二进制位全部左移指定的位数,左 边的二进制位丢弃,右边补 0。 | ❎ |
|
||
| `>>` | 二进制右移 | 2 | 将一个数的各二进制位全部右移指定的位数,正数左补 0,负数左补 1,右边丢弃。 | ❎ |
|
||
|
||
> [!NOTE]
|
||
>
|
||
> 操作数在进行位运算的时候,以它的补码形式计算!!!
|
||
|
||
## 6.2 输出二进制位
|
||
|
||
* 在 C 语言中,`printf` 是没有提供输出二进制位的格式占位符的;但是,我们可以手动实现,以方便后期操作。
|
||
|
||
|
||
|
||
* 示例:
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
/**
|
||
* 获取指定整数的二进制表示
|
||
* @param num 整数
|
||
* @return 二进制表示的字符串,不包括前导的 '0b' 字符
|
||
*/
|
||
char* getBinary(int num) {
|
||
static char binaryString[33];
|
||
int i, j;
|
||
|
||
for (i = sizeof(num) * 8 - 1, j = 0; i >= 0; i--, j++) {
|
||
const int bit = (num >> i) & 1;
|
||
binaryString[j] = bit + '0';
|
||
}
|
||
|
||
binaryString[j] = '\0';
|
||
return binaryString;
|
||
}
|
||
|
||
int main() {
|
||
|
||
int a = 17;
|
||
int b = -12;
|
||
|
||
printf("整数 %d 的二进制表示:%s \n", a, getBinary(a));
|
||
printf("整数 %d 的二进制表示:%s \n", b, getBinary(b));
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
## 6.3 按位与
|
||
|
||
* 按位与 `&` 的运算规则是:如果二进制对应的位上都是 1 才是 1 ,否则为 0 ,即:
|
||
* `1 & 1` 的结果是 `1` 。
|
||
* `1 & 0` 的结果是 `0` 。
|
||
* `0 & 1` 的结果是 `0` 。
|
||
* `0 & 0` 的结果是 `0` 。
|
||
|
||
|
||
|
||
* 示例:`9 & 7 = 1`
|
||
|
||
![](./assets/22.svg)
|
||
|
||
|
||
|
||
* 示例:`-9 & 7 = 7`
|
||
|
||
![](./assets/23.svg)
|
||
|
||
## 6.4 按位或
|
||
|
||
* 按位与 `|` 的运算规则是:如果二进制对应的位上只要有 1 就是 1 ,否则为 0 ,即:
|
||
* `1 | 1` 的结果是 `1` 。
|
||
* `1 | 0` 的结果是 `1` 。
|
||
* `0 | 1` 的结果是 `1` 。
|
||
* `0 | 0` 的结果是 `0` 。
|
||
|
||
|
||
|
||
* 示例:`9 | 7 = 15`
|
||
|
||
![](./assets/24.svg)
|
||
|
||
|
||
|
||
* 示例:`-9 | 7 = -9`
|
||
|
||
![](./assets/25.svg)
|
||
|
||
## 6.5 按位异或
|
||
|
||
* 按位与 `^` 的运算规则是:如果二进制对应的位上一个为 1 一个为 0 就为 1 ,否则为 0 ,即:
|
||
* `1 ^ 1` 的结果是 `0` 。
|
||
* `1 ^ 0` 的结果是 `1` 。
|
||
* `0 ^ 1` 的结果是 `1` 。
|
||
* `0 ^ 0` 的结果是 `0` 。
|
||
|
||
> [!NOTE]
|
||
>
|
||
> 按位异或的场景有:
|
||
>
|
||
> * ① 交换两个数值:异或操作可以在不使用临时变量的情况下交换两个变量的值。
|
||
> * ② 加密或解密:异或操作用于简单的加密和解密算法。
|
||
> * ③ 错误检测和校正:异或操作可以用于奇偶校验位的计算和检测错误(RAID-3 以及以上)。
|
||
> * ……
|
||
|
||
|
||
|
||
* 示例:`9 ^ 7 = 14`
|
||
|
||
![](./assets/26.svg)
|
||
|
||
|
||
|
||
* 示例:`-9 ^ 7 = -16`
|
||
|
||
![](./assets/27.svg)
|
||
|
||
## 6.6 按位取反
|
||
|
||
* 运算规则:如果二进制对应的位上是 1,则结果为 0;如果是 0 ,则结果为 1 。
|
||
* `~0` 的结果是 `1` 。
|
||
* `~1` 的结果是 `0` 。
|
||
|
||
|
||
|
||
* 示例:`~9 = -10`
|
||
|
||
![](./assets/28.svg)
|
||
|
||
|
||
|
||
* 示例:`~-9 = 8`
|
||
|
||
![](./assets/29.svg)
|
||
|
||
## 6.7 二进制左移
|
||
|
||
* 在一定范围内,数据每向左移动一位,相当于原数据 × 2。(正数、负数都适用)
|
||
|
||
|
||
|
||
* 示例:`3 << 4 = 48` (3 × 2^4)
|
||
|
||
![](./assets/30.svg)
|
||
|
||
|
||
|
||
* 示例:`-3 << 4 = -48` (-3 × 2 ^4)
|
||
|
||
![](./assets/31.svg)
|
||
|
||
## 6.8 二进制右移
|
||
|
||
* 在一定范围内,数据每向右移动一位,相当于原数据 ÷ 2。(正数、负数都适用)
|
||
|
||
> [!NOTE]
|
||
>
|
||
> * ① 如果不能整除,则向下取整。
|
||
> * ② 右移运算符最好只用于无符号整数,不要用于负数。因为不同系统对于右移后如何处理负数的符号位,有不同的做法,可能会得到不一样的结果。
|
||
|
||
|
||
|
||
* 示例:`69 >> 4 = 4` (69 ÷ 2^4 )
|
||
|
||
![](./assets/32.svg)
|
||
|
||
|
||
|
||
* 示例:`-69 >> 4 = -5` (-69 ÷ 2^4 )
|
||
|
||
![](./assets/33.svg)
|
||
|
||
# 第七章:三元运算符(⭐)
|
||
|
||
## 7.1 概述
|
||
|
||
* 语法:
|
||
|
||
```c
|
||
条件表达式 ? 表达式1 : 表达式2 ;
|
||
```
|
||
|
||
> [!NOTE]
|
||
>
|
||
> * 如果条件表达式为非 0 (真),则整个表达式的值是表达式 1 。
|
||
> * 如果条件表达式为 0 (假),则整个表达式的值是表达式 2 。
|
||
|
||
## 7.2 应用示例
|
||
|
||
* 示例:
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
int main() {
|
||
|
||
int m = 110;
|
||
int n = 20;
|
||
int result = m > n ? m : n;
|
||
printf("result = %d\n", result); // result = 110
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
# 第八章:运算符的优先级和结合性(⭐)
|
||
|
||
## 7.1 概述
|
||
|
||
* 在数学中,如果一个表达式是 `a + b * c` ,我们知道其运算规则就是:先算乘除再算加减。其实,在 C 语言中也是一样的,先算乘法再算加减,即:C 语言中乘除的运算符比加减的运算符的优先级要高。
|
||
|
||
## 7.2 优先级和结合性
|
||
|
||
* `优先级`和`结合性`的定义,如下所示:
|
||
* ① 所谓的`优先级`:就是当多个运算符出现在同一个表达式中时,先执行哪个运算符。
|
||
* ② 所谓的`结合性`:就是当多个相同优先级的运算符出现在同一个表达式中的时候,是从左到右运算,还是从右到左运算。
|
||
* `左结合性`:具有相同优先级的运算符将`从左到右`(➡️)进行计算。
|
||
* `右结合性`:具有相同优先级的运算符将`从右到左`(⬅️)进行计算。
|
||
|
||
> [!NOTE]
|
||
>
|
||
> 技巧:先看`优先级`;如果优先级相同,再看`结合性`。
|
||
|
||
* C 语言中运算符的优先级有几十个,有的运算符优先级不同,有的运算符优先级相同,如下所示:
|
||
|
||
| 优先级 | 运算符 | 名称或含义 | 结合方向 |
|
||
| ------ | -------------- | ------------------------------------------- | ------------- |
|
||
| `0` | `()` | 小括号,最高优先级 | ➡️(从左到右) |
|
||
| `1` | `++`、`--` | 后缀自增和自减,如:`i++`、`i--` 等 | ➡️(从左到右) |
|
||
| | `()` | 小括号,函数调用,如:`sum(1,2)` 等 | |
|
||
| | `[]` | 数组下标,如:`arr[0]`、`arr[1]` 等 | |
|
||
| | `.` | 结构体或共用体成员访问 | |
|
||
| | `->` | 结构体或共用体成员通过指针访问 | |
|
||
| `2` | `++`、`--` | 前缀自增和自减,如:`++i`、`--i` 等 | ⬅️(从右到左) |
|
||
| | `+` | 一元加运算符,表示操作数的正,如:`+2` 等 | |
|
||
| | `-` | 一元减运算符,表示操作数的负,如:`-3` 等 | |
|
||
| | `!` | 逻辑非运算符(逻辑运算符) | |
|
||
| | `~` | 按位取反运算符(位运算符) | |
|
||
| | `(typename)` | 强制类型转换 | |
|
||
| | `*` | 解引用运算符 | |
|
||
| | `&` | 取地址运算符 | |
|
||
| | `sizeof` | 取大小运算符 | |
|
||
| `3` | `/` | 除法运算符(算术运算符) | ➡️(从左到右) |
|
||
| | `*` | 乘法运算符(算术运算符) | |
|
||
| | `%` | 取模(取余)运算符(算术运算符) | |
|
||
| `4` | `+` | 二元加运算符(算术运算符),如:`2 + 3` 等 | ➡️(从左到右) |
|
||
| | `-` | 二元减运算符(算术运算符),如:`3 - 2` 等 | |
|
||
| `5` | `<<` | 左移位运算符(位运算符) | ➡️(从左到右) |
|
||
| | `>>` | 右移位运算符(位运算符) | |
|
||
| `6` | `>` | 大于运算符(比较运算符) | ➡️(从左到右) |
|
||
| | `>=` | 大于等于运算符(比较运算符) | |
|
||
| | `<` | 小于运算符(比较运算符) | |
|
||
| | `<=` | 小于等于运算符(比较运算符) | |
|
||
| `7` | `==` | 等于运算符(比较运算符) | ➡️(从左到右) |
|
||
| | `!=` | 不等于运算符(比较运算符) | |
|
||
| `8` | `&` | 按位与运算符(位运算符) | ➡️(从左到右) |
|
||
| `9` | `^` | 按位异或运算符(位运算符) | ➡️(从左到右) |
|
||
| `10` | `\|` | 按位或运算符(位运算符) | ➡️(从左到右) |
|
||
| `11` | `&&` | 逻辑与运算符(逻辑运算符) | ➡️(从左到右) |
|
||
| `12` | `\|\|` | 逻辑或运算符(逻辑运算符) | ➡️(从左到右) |
|
||
| `13` | `?:` | 三目(三元)运算符 | ⬅️(从右到左) |
|
||
| `14` | `=` | 简单赋值运算符(赋值运算符) | ⬅️(从右到左) |
|
||
| | `/=` | 除后赋值运算符(赋值运算符) | |
|
||
| | `*=` | 乘后赋值运算符(赋值运算符) | |
|
||
| | `%=` | 取模后赋值运算符(赋值运算符) | |
|
||
| | `+=` | 加后赋值运算符(赋值运算符) | |
|
||
| | `-=` | 减后赋值运算符(赋值运算符) | |
|
||
| | `<<=` | 左移后赋值运算符(赋值运算符) | |
|
||
| | `>>=` | 右移后赋值运算符(赋值运算符) | |
|
||
| | `&=` | 按位与后赋值运算符(赋值运算符) | |
|
||
| | `^=` | 按位异或后赋值运算符(赋值运算符) | |
|
||
| | `\|=` | 按位或后赋值运算符(赋值运算符) | |
|
||
| `15` | `,` | 逗号运算符 | ➡️(从左到右) |
|
||
|
||
> [!WARNING]
|
||
>
|
||
> * ① 不要过多的依赖运算符的优先级来控制表达式的执行顺序,这样可读性太差,尽量`使用小括号来控制`表达式的执行顺序。
|
||
> * ② 不要把一个表达式写得过于复杂,如果一个表达式过于复杂,则把它`分成几步`来完成。
|
||
> * ③ 运算符优先级不用刻意地去记忆,总体上:一元运算符 > 算术运算符 > 关系运算符 > 逻辑运算符 > 三元运算符 > 赋值运算符。
|