mirror of
https://github.com/Aexiar/c.git
synced 2024-10-22 14:05:45 +02:00
23 KiB
23 KiB
第一章:相关概念
1.1 运算符、表达式和操作数
- 运算符是一种特殊的符号,用于数据的运算、赋值和比较等。
表达式
指的是一组运算数、运算符的组合,表达式一定具有值
,一个变量或一个常量可以是表达式,变量、常量和运算符也可以组成表达式,如:
操作数
指的是参与运算
的值
或者对象
,如:
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 应用示例
- 示例:正号和负号
#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;
}
- 示例:加、减、乘、除(整数之间做除法时,结果只保留整数部分而舍弃小数部分)、取模
#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;
}
- 示例:取模(运算结果的符号与被模数也就是第一个操作数相同。)
#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;
}
- 示例:自增和自减
#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;
- 示例:
#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(真)表示布尔类型的值。
- ② 不要将
==
写成=
,==
是比较运算符,而=
是赋值运算符。- ③
>=
或<=
含义是只需要满足大于或等于
、小于或等于
其中一个条件,结果就返回真。
- 示例:
#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 应用示例
- 示例:
#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;
}
- 示例:
#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;
}
- 示例:
#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 应用示例
- 示例:
#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
是没有提供输出二进制位的格式占位符的;但是,我们可以手动实现,以方便后期操作。 -
示例:
#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
- 示例:
-9 & 7 = 7
6.4 按位或
-
按位与
|
的运算规则是:如果二进制对应的位上只要有 1 就是 1 ,否则为 0 ,即:1 | 1
的结果是1
。1 | 0
的结果是1
。0 | 1
的结果是1
。0 | 0
的结果是0
。
-
示例:
9 | 7 = 15
- 示例:
-9 | 7 = -9
6.5 按位异或
- 按位与
^
的运算规则是:如果二进制对应的位上一个为 1 一个为 0 就为 1 ,否则为 0 ,即:1 ^ 1
的结果是0
。1 ^ 0
的结果是1
。0 ^ 1
的结果是1
。0 ^ 0
的结果是0
。
Note
按位异或的场景有:
- ① 交换两个数值:异或操作可以在不使用临时变量的情况下交换两个变量的值。
- ② 加密或解密:异或操作用于简单的加密和解密算法。
- ③ 错误检测和校正:异或操作可以用于奇偶校验位的计算和检测错误(RAID-3 以及以上)。
- ……
- 示例:
9 ^ 7 = 14
- 示例:
-9 ^ 7 = -16
6.6 按位取反
-
运算规则:如果二进制对应的位上是 1,则结果为 0;如果是 0 ,则结果为 1 。
~0
的结果是1
。~1
的结果是0
。
-
示例:
~9 = -10
- 示例:
~-9 = 8
6.7 二进制左移
-
在一定范围内,数据每向左移动一位,相当于原数据 × 2。(正数、负数都适用)
-
示例:
3 << 4 = 48
(3 × 2^4)
- 示例:
-3 << 4 = -48
(-3 × 2 ^4)
6.8 二进制右移
- 在一定范围内,数据每向右移动一位,相当于原数据 ÷ 2。(正数、负数都适用)
Note
- ① 如果不能整除,则向下取整。
- ② 右移运算符最好只用于无符号整数,不要用于负数。因为不同系统对于右移后如何处理负数的符号位,有不同的做法,可能会得到不一样的结果。
- 示例:
69 >> 4 = 4
(69 ÷ 2^4 )
- 示例:
-69 >> 4 = -5
(-69 ÷ 2^4 )
第七章:三元运算符(⭐)
7.1 概述
- 语法:
条件表达式 ? 表达式1 : 表达式2 ;
Note
- 如果条件表达式为非 0 (真),则整个表达式的值是表达式 1 。
- 如果条件表达式为 0 (假),则整个表达式的值是表达式 2 。
7.2 应用示例
- 示例:
#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
- ① 不要过多的依赖运算符的优先级来控制表达式的执行顺序,这样可读性太差,尽量
使用小括号来控制
表达式的执行顺序。- ② 不要把一个表达式写得过于复杂,如果一个表达式过于复杂,则把它
分成几步
来完成。- ③ 运算符优先级不用刻意地去记忆,总体上:一元运算符 > 算术运算符 > 关系运算符 > 逻辑运算符 > 三元运算符 > 赋值运算符。