点关注不迷路哟。你的点赞、收藏,一键三连,是我持续更新的动力哟!!!
主页:
一位搞嵌入式的 genius-CSDN博客https://blog.csdn.net/m0_73589512?spm=1000.2115.3001.5343
目录
[short 类型](#short 类型)
[short 类型与 unsigned short 类型的定义](#short 类型与 unsigned short 类型的定义)
[short 类型与 int 类型的长度区别](#short 类型与 int 类型的长度区别)
[short 类型值域的分析](#short 类型值域的分析)
[int 类型](#int 类型)
[limits.h 头文件](#limits.h 头文件)
[static 存储类型](#static 存储类型)
[1)static 局部变量示例](#1)static 局部变量示例)
[2)static 局部变量的特点](#2)static 局部变量的特点)
[extern 存储类型](#extern 存储类型)
[1)extern 全局变量示例](#1)extern 全局变量示例)
[1)C 语言提供的算术运算符](#1)C 语言提供的算术运算符)
[例题:double 类型运算](#例题:double 类型运算)
[1)C 语言的关系运算符](#1)C 语言的关系运算符)
[1)逻辑非 ! 运算符的运算律](#1)逻辑非 ! 运算符的运算律)
[2)逻辑与 && 运算符的运算规律](#2)逻辑与 && 运算符的运算规律)
[例题:if 条件语句逻辑与运算](#例题:if 条件语句逻辑与运算)
[3)逻辑或 || 运算符的运算规律](#3)逻辑或 || 运算符的运算规律)
[1. 算术运算符](#1. 算术运算符)
[2. 关系运算符](#2. 关系运算符)
[3. 逻辑运算符](#3. 逻辑运算符)
[4. 例题:逻辑运算求值](#4. 例题:逻辑运算求值)
[1)C 语言的位运算符](#1)C 语言的位运算符)
[1. 十六进制与二进制转换](#1. 十六进制与二进制转换)
[2. 左移运算原理](#2. 左移运算原理)
[3. 例题:验证左移运算](#3. 例题:验证左移运算)
[4. 移位运算的数学本质](#4. 移位运算的数学本质)
[1. 思考](#1. 思考)
[2. 例题:无符号数置 1](#2. 例题:无符号数置 1)
[3. 例题:无符号数清 0](#3. 例题:无符号数清 0)
[4. 例题:十进制转十六进制(位运算优化)](#4. 例题:十进制转十六进制(位运算优化))
[3)sizeof 运算符](#3)sizeof 运算符)
Day02→数据类型(上)
数据类型分类
基本数据类型
-
分类依据:根据存储范围和正负特性选择数据类型,主要考虑数据大小范围和是否需要存储负数。
-
主要类型:
-
整形:包括有符号(signed)和无符号(unsigned)两种形式。
-
字符型:char 类型,实际也分有符号和无符号两种。
-
实型:float 和 double 用于存储小数。
-
枚举型:enum 类型。
-
-
存储特性:
-
有符号数可存储正负数,无符号数只能存储≥0 的数。
-
范围从小到大:short < int < long < long long。
-
整形数据类型
-
符号区分:
-
有符号数:用 signed 表示,可存储正负数。
-
无符号数:用 unsigned 表示,只能存储≥0 的数。
-
-
范围区分:
-
short:短整型。
-
int:基本整型。
-
long:长整型。
-
long long:超长整型。
-
-
选择原则:
-
根据数据是否可能为负数选择有 / 无符号。
-
根据数据大小范围选择合适的数据类型。
-
字符型数据类型
-
基本形式:char 类型。
-
特殊性质:
-
实际上也分为有符号和无符号两种形式。
-
字符的 ASCII 码值可以视为某种形式的整型。
-
-
存储特性:
-
可存储单个字符。
-
底层存储的是字符对应的 ASCII 码值。
-
实型数据类型
-
主要类型:
-
float:单精度浮点型。
-
double:双精度浮点型。
-
-
应用场景:
-
用于存储小数数据。
-
需要更高精度时使用 double 类型。
-
构造数据类型
-
定义:用户自定义的数据类型,非基础类型。
-
主要类型:
-
数组。
-
结构体(struct)。
-
共用体(union)。
-
-
特点:
-
需要用户自行定义。
-
可以组合基本类型构建更复杂的数据结构。
-
特殊数据类型
-
指针类型:
-
用于存储内存地址。
-
是 C 语言的重要特性。
-
-
空类型(void):
-
用于说明不返回值的函数。
-
也可用于指向类型的指针。
-
布尔类型详解
基本概念
-
类型名称:bool。
-
值域:
-
true(非零值,通常为 1)。
-
false(零值)。
-
-
赋值方式:
-
直接赋值为 true/false。
-
赋值为非零 / 零值。
-
-
判断规则:非零值为真,零值为假。
使用注意事项
-
头文件要求:
-
需要包含
<stdbool.h>
头文件。 -
否则会报 "unknown type name" 错误。
-
-
底层实现:
-
实际是 C99 标准引入的
_Bool
类型。 -
true 和 false 是宏定义,分别为 1 和 0。
-
-
赋值特性:
-
赋值为负数(-1)时,仍视为真值(非零)。
-
但实际存储时会转换为 1。
-
预处理分析
-
预处理命令 :
gcc -E bool.c -o bool.i
。 -
头文件位置 :
/usr/lib/gcc/x86_64-linux-gnu/11/include/stdbool.h
。 -
宏定义内容:
#define bool _Bool #define true 1 #define false 0
-
替代方案:
-
可直接使用
_Bool
类型。 -
但仍需头文件来使用 true/false。
-
知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
数据类型概述 | C 语言学习路径:数据类型→常量 / 变量→运算符→控制语句→输入输出→数组 / 字符串→指针 / 函数 | 数据类型分类逻辑(基本类型 / 构造类型 / 指针类型 / 空类型) | ⭐⭐ |
布尔类型 | - 需引入 stdbool.h 头文件;- 本质为_Bool 基本类型(C99 新增);- 真值规则:非零为真(true=1),零为假(false=0) | - 未引入头文件时直接使用 bool 会报错;- 宏定义陷阱:true/false 实际为头文件定义的宏(值固定为 1/0) | ⭐⭐⭐ |
整型分类逻辑 | 根据范围(short/int/long/long long)和符号性(signed/unsigned)划分 | - long long 与 long 范围差异;- 字符型本质属于特殊整型(可参与数值运算) | ⭐⭐⭐⭐ |
浮点型 | float(单精度)与 double(双精度)的存储精度差异 | 默认小数常量类型为 double(需加 f 后缀显式声明为 float) | ⭐⭐ |
类型转换 | - 隐式转换规则(小类型→大类型);- 强制转换语法:(目标类型) 变量 | 符号扩展问题(如 char 转 int 时符号位填充) | ⭐⭐⭐ |
编程实践要点 | - 强调同步编码验证(如布尔值打印测试);- 预处理命令 gcc -E 分析头文件展开 | 易错点:布尔变量赋值为 - 1 时实际存储值为 1(因宏替换) |
Day02→数据类型(下)
char类型
1 #include <stdio.h>
2 #include <stdbool.h>
3
4 // argc:命令行参数的个数(包含程序名本身)
5
6 int main(int argc, char *argv[]) {
7 unsigned char ch;
8 ch = -1;
9 printf("ch = %d %c\n",ch,ch);
10
11 return 0;
12
13 }
那这份代码为什么最后打印出来的是255???
解答:
在这段代码中,打印出 255 主要和 unsigned char 类型以及数据的存储、类型转换规则有关,以下是详细解释:
1. 数据存储
char 类型如果是有符号的,其取值范围是 -128 到 127,如果是 unsigned char,取值范围是 0 到 255 。在代码中,变量 ch 被声明为 unsigned char 类型。
当执行 ch = -1; 时,-1 是一个有符号整数,在计算机中,有符号整数以补码形式存储,-1 的 8 位补码是 1111 1111 。将 -1 赋值给 unsigned char 类型的变量 ch 时,编译器会直接把 -1 的补码值 1111 1111 存储到 ch 对应的内存空间中 。因为 unsigned char 类型不区分符号位,所以这个 1111 1111 就被解释为无符号数 。
2. 类型转换与打印
printf 函数中,%d 格式说明符用于打印整数。当使用 %d 打印 unsigned char 类型的变量 ch 时,会发生类型提升。由于 unsigned char 是小于 int 类型的整型,在表达式求值时,会将 unsigned char 类型的值提升为 int 类型 。
对于 unsigned char 类型,在提升过程中,会在高位补 0 ,1111 1111 提升为 int 类型后就是 0000 0000 0000 0000 0000 0000 1111 1111 ,对应的无符号十进制值就是 255 ,所以打印出 255 。
而对于 %c 格式说明符,它会将 ch 的值作为 ASCII 码值来解析对应的字符。ASCII 码中,码值 255 对应的字符是不可打印的控制字符,所以通常会看到一个乱码或者不显示字符 。
总结来说,是 unsigned char 类型对数据的解释方式,以及 printf 函数在打印时的类型提升规则,导致了打印出 255 这个结果。
short 类型
short 类型与 unsigned short 类型的定义
-
存储长度:short 类型占用 2 个字节存储空间。
-
有符号范围:signed short 值域为 - 32768~32767。
-
无符号范围:unsigned short 值域为 0~65535。
short 类型与 int 类型的长度区别
-
操作系统影响:在 32 位系统中,short 长度为 2 字节,int 长度为 4 字节。
-
符号处理:两者都可以存储负数,使用 signed 修饰符表示有符号类型。
示例:打印类型长度
-
测量方法:使用 sizeof 运算符获取类型长度。
-
典型结果:
-
bool 类型:1 字节
-
char 类型:1 字节
-
short 类型:2 字节
-
int 类型:4 字节
-
-
实现限制:sizeof 最小单位为字节,不能测量更小的位数。
short 类型值域的分析
-
位数计算:2 字节 = 16 位,无符号最大值为(2^{16}-1=65535)。
-
有符号范围:-32768 到 32767,最高位为符号位。
-
应用场景:适合存储 6 万以下的数值,如工资等中等规模数据。
int 类型
-
存储长度:固定占用 4 字节存储空间。
-
无符号范围:0~4294967295((2^{32}-1))。
-
有符号范围:-2147483648~2147483647。
-
空间规模:相当于 4GB 的寻址空间。
-
计算技巧:可通过(2^{n}-1)(n 为总位数)快速估算最大取值范围。
limits.h 头文件
头文件作用
-
数据类型范围查询:当数据类型长度较长时(如 int、long 等),直接记忆其值域范围较为困难,limits.h 头文件提供了各数据类型的范围宏定义。
-
避免手动计算:通过包含该头文件可直接使用预定义的常量值,无需手动计算数据类型的边界值。
文件位置与包含方式
-
标准位置:位于 /usr/include/ 目录下。
-
包含语法 :
#include <limits.h>
。
主要宏定义内容
-
字符型范围:
-
有符号 char: -128~127(SCHAR_MIN~SCHAR_MAX)
-
无符号 char: 0~255(UCHAR_MAX)
-
字节定义: CHAR_BIT 宏定义为 8,表示 1 字节 = 8 位
-
-
short 类型范围:
-
有符号 short: -32768~32767(SHRT_MIN~SHRT_MAX)
-
无符号 short: 0~65535(USHRT_MAX)
-
-
整型范围:
-
有符号 int: -2147483648~2147483647(INT_MIN~INT_MAX)
-
无符号 int: 0~4294967295(UINT_MAX)
-
-
long 类型范围:
-
有符号 long: -9223372036854775808~9223372036854775807(LONG_MIN~LONG_MAX)
-
无符号 long: 0~18446744073709551615(ULONG_MAX)
-
实际应用示例
- 程序演示:
#include <stdio.h>
#include <limits.h>
int main() {
printf("有符号int最小值:%d\n", INT_MIN);
printf("有符号int最大值:%d\n", INT_MAX);
printf("无符号int最大值:%u\n", UINT_MAX);
return 0;
}
- 输出结果:
有符号int最小值:-2147483648
有符号int最大值:2147483647
无符号int最大值:4294967295
使用建议
-
编程实践:在需要获取数据类型边界值时直接使用 limits.h 中的宏定义。
-
记忆技巧:只需记住各类型字节长度,具体范围可通过头文件查询。
-
验证方法:编写简单测试程序打印各宏定义值,与理论值进行比对。
知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
布尔类型 | 基本数据类型介绍,可赋值的范围及长度 | 布尔类型的实际存储方式(通过整型实现) | ⭐⭐ |
字符型 (char) | 1 字节长度,有符号 (-128~127) 和无符号 (0~255) 范围 | 负 128 的特殊补码表示 (10000000),ASCII 码转换规则 | ⭐⭐⭐⭐ |
整型 (short/int) | short (2 字节) 范围:-32768~32767,int (4 字节) 范围:-2147483648~2147483647 | 32/64 位系统下 long 类型长度差异,使用 sizeof 测试环境 | ⭐⭐⭐ |
数据范围计算 | 二进制补码原理,原码 / 反码 / 补码转换 | 负数的补码计算规则,边界值溢出问题(如 128 变成 - 128) | ⭐⭐⭐⭐ |
limits.h 头文件 | 预定义各类型极值常量(CHAR_MIN/CHAR_MAX 等) | 实际开发中替代手动计算范围的方法 | ⭐⭐ |
类型安全赋值 | 越界的隐式转换风险(如 char 赋 128 转为 - 128) | 编译器不报错但运行异常的情况处理 | ⭐⭐⭐⭐ |
ASCII 编码 | 字符与整数的映射关系(如 65 对应 'A') | 非打印字符的显示问题(如 12 |
Day02→常量
常量定义
-
概念:常量是在整个程序运行期间值不会发生变化的数据。
-
特点:与变量相对,常量值在定义后不可修改。
-
分类依据:基于基本数据类型进行分类。
基本数据类型常量
-
基础:依托于基本数据类型(整型、浮点型等)的常量表示。
-
赋值规范:不同数据类型对应的常量值有特定书写格式要求。
-
重要性:是编程基础且使用频率极高的知识点。
1)整型常量
-
表示方法+:
-
十进制:直接书写数字,如
123
。 -
八进制:以
0
开头,如0123
(表示十进制83
)。 -
十六进制:以
0x
开头,如0x1A
(表示十进制26
)。
-
2)浮点型常量
-
标准形式 :必须包含小数点或指数,如
3.14
或3e5
(3×10⁵
)。 -
科学计数法 :应写作
1.23e4
(1.23×10⁴
)。 -
默认类型 :未加后缀默认为
double
类型(加f
后缀为float
,如3.14f
)。
标识常量(宏定义)
-
别称 :也称为宏定义(
#define
)。 -
作用:用标识符代表常量值,提高代码可读性。
-
语法格式 :
#define 标识符 常量值
(如#define PI 3.14159
)。 -
预处理特性:在编译前进行文本替换,不占用内存。
1)使用规范
-
命名约定 :通常使用全大写字母,如
#define MAX 100
。 -
作用范围:从定义处开始到文件结束。
-
类型安全:宏定义不进行类型检查,仅做纯文本替换。
2)优势与局限
-
优势:
-
便于集中修改常量值(如修改
PI
精度只需改定义处)。 -
避免 "魔法数字"(直接写
3.14
等无意义数值),提高代码可维护性。
-
-
局限:
-
不占用内存空间(无存储地址,调试时无法查看值)。
-
替换可能引发逻辑错误(如
#define TWO 1+1
,TWO*2
会被替换为1+1*2=3
,需加括号#define TWO (1+1)
)。
-
基本数据类型的常量(补充细节)
1)整型常量
-
进制表示细节:
-
八进制前缀
0
和十六进制前缀0x
不可省略(如077
是八进制63
,0x77
是十六进制119
)。 -
二进制转换关系:1 个八进制数对应 3 位二进制,1 个十六进制数对应 4 位二进制(如
0xF
对应1111
)。 -
注意:C 语言不支持直接写二进制常量(需用
0b
前缀的编译器扩展,非标准)。
-
2)浮点常量
-
精度分类:
-
单精度(
float
):占 4 字节,精度约 6-7 位小数。 -
双精度(
double
):占 8 字节,精度约 15-17 位小数(默认类型)。
-
-
指数形式规则 :尾数可省略整数 / 小数部分(如
.5e3
等价0.5×10³
,5.e-2
等价5.0×10⁻²
)。
3)指数常量
-
结构组成 :尾数 +
e/E
+ 指数(如1.23e-4
表示1.23×10⁻⁴
)。 -
应用场景:
-
特别大的数(如地球质量
5.97e24
千克)。 -
特别小的数(如电子电荷量
1.602e-19
库仑)。
-
-
优势 :避免书写大量零,表达更简洁(
0.0000000035
可写为3.5e-9
)。
4)字符常量
-
定义 :由单引号括起的单个字符,如
'A'
、'0'
、'+'
。 -
本质 :存储的是对应 ASCII 码值(如
'A'
实际存储65
,'0'
存储48
)。 -
表示方式:
-
直接写字符:
'a'
(ASCII 码97
)。 -
用 ASCII 码值:
97
(等价于'a'
)。
-
-
运算特性:
可参与整数运算(实际是对 ASCII 码值运算),例如:
-
大写转小写:
'A' + 32 = 'a'
(65 + 32 = 97
)。 -
字符数字转数值:
'5' - 48 = 5
(53 - 48 = 5
)。
-
5)字符串常量
-
定义 :用双引号括起的字符序列,如
"Hello"
、"123"
。 -
存储特点 :自动以
'\0'
(空字符,ASCII 码0
)结尾,例如"a"
实际存储'a'
和'\0'
,共 2 字节。 -
与字符常量的区别:
类型 表示符号 存储内容 示例对比 字符常量 单引号 单个字符(1 字节) 'a'
占 1 字节字符串常量 双引号 字符序列 + '\0'
"a"
占 2 字节('a'+'\0'
)
应用案例(精选)
例题:字符常量运算
#include <stdio.h>
int main() {
char c1 = 'B' + 3; // 'B'(66) + 3 = 69 → 'E'
char c2 = 'b' - 32; // 'b'(98) - 32 = 66 → 'B'
int num = '9' - '0'; // '9'(57) - '0'(48) = 9
printf("%c %c %d\n", c1, c2, num); // 输出:E B 9
return 0;
}
例题:宏定义的文本替换
#include <stdio.h>
#define ONE 1
#define TWO ONE + ONE // 注意:未加括号
int main() {
int a = 2;
int res = TWO * a; // 替换为 1 + 1 * 2 → 3(非预期的 4)
printf("%d\n", res);
return 0;
}
// 修正:#define TWO (ONE + ONE),则 res = (1+1)*2 = 4
知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
基本数据类型常量 | 整型(十进制 / 八进制 / 十六进制)、浮点型(常规 / 科学计数法) | 八进制以 0 开头、十六进制以 0x 开头;科学计数法中 e 代表 10 的幂 |
★★☆☆☆ |
字符常量 | 单引号表示,ASCII 码转换(大小写差 32 ,数字字符转数值差 48) | '0' (ASCII 48)≠ 0(数值);大写转小写需加 32,而非直接改字母 |
★★★☆☆ |
字符串常量 | 双引号表示,隐含 '\0' 结尾 |
"a" 占 2 字节(含 '\0' ),与 'a' (1 字节)的区别 |
★★★☆☆ |
标识常量(宏) | #define 定义,预处理文本替换 |
宏展开不做运算(需加括号避免歧义);无类型检查,可能引发隐蔽错误 | ★★★★☆ |
常量综合应用 | 科学计数法表示极值、宏定义提高可维护性(如水分子数量计算) | 浮点数精度问题;宏替换后的运算符 |
Day02→嵌入式开发变量(一)
作业讲解
-
作业要求:编写程序计算指定夸脱水中的水分子数量。
-
解题思路:
-
使用宏定义常量:水分子质量(如
3.0e-23g
)和 1 夸脱质量(950g
)。 -
计算公式:总水分子数 = (夸脱数
n
× 950) / 水分子质量。 -
使用浮点数类型(
float
)存储变量。 -
输出采用科学计数法格式(
%e
)。
-
-
实现要点:
-
需处理极小值(水分子质量)和极大值(计算结果)的数值范围。
-
注意
scanf
读取浮点数时应使用%f
格式而非%d
。
-
变量的基础
1)变量的概念
-
命名规则:
-
组成:字母、数字、下划线。
-
限制:不能以数字开头,不能与关键字重名(如
int
、if
等)。
-
-
本质:程序中的存储空间抽象,用于存放可变化的数据。
-
地址特性 :变量首地址可通过
&
运算符获取(如&num
表示变量num
的地址)。
2)变量的存储空间
-
空间分配:
-
大小由数据类型决定(如
int
占 4 字节,char
占 1 字节),与变量值无关。 -
运行时实际占用内存空间,空间大小可通过
sizeof(变量名)
查看。
-
-
地址特性 :变量首地址可通过
&
运算符获取,用于指针操作等场景。
变量的说明
-
标准形式 :
<存储类型> <数据类型> <变量名>
示例:auto int age;
、static float score;
-
存储类型 :
auto
、register
、static
、extern
(决定变量的存储方式和生命周期)。 -
数据类型 :基本类型(如
int
、float
)或自定义类型(如结构体、数组)。 -
默认情况 :存储类型
auto
可省略(如int a;
等价于auto int a;
)。
变量的存储类型
1)auto
-
属性:局部变量,默认存储类型。
-
作用范围 :限定在函数体或复合语句块(如
if
、for
块)内。 -
生命周期:进入作用域时创建,离开作用域时自动销毁。
-
默认值:未初始化时为随机值(需手动初始化后使用)。
-
应用案例:auto 变量使用示例
#include <stdio.h> int main() { int a = 10; // 省略auto,默认是auto变量 if (a > 5) { auto int b = 20; // 块内auto变量 printf("b = %d\n", b); // 输出:20 } // printf("b = %d\n", b); // 错误:b在if块外不可访问 return 0; }
-
注意事项:
-
局部变量作用域严格受限,跨作用域访问会报错。
-
使用前必须初始化,否则可能读取到随机垃圾值。
-
2)register
-
设计目的:建议编译器将频繁访问的变量存储在 CPU 寄存器中,以加速访问(寄存器访问速度远快于内存)。
-
硬件限制:
-
寄存器数量有限(32 位 CPU 通常仅几十个)。
-
数据长度限制(32 位 CPU 最大支持 4 字节,64 位 CPU 最大支持 8 字节)。
-
-
实现机制:
- 编译器可忽略该建议(若寄存器不足或变量不适合存寄存器),自动转为
auto
类型。
- 编译器可忽略该建议(若寄存器不足或变量不适合存寄存器),自动转为
-
使用注意事项:
-
类型限制 :必须是 CPU 可直接处理的类型(如
int
、char
),长度不超过整型(通常 4 字节)。 -
操作限制 :不能使用
&
取地址(寄存器无内存地址),无法通过指针访问。 -
性能考量:过度使用可能导致寄存器资源紧张,反而降低性能,仅适用于高频访问的小型变量(如循环计数器)。
-
示例:
#include <stdio.h> int main() { register int i; // 建议将i存入寄存器(适合循环计数) for (i = 0; i < 1000000; i++) { // 频繁使用i,寄存器存储可加速循环 } // &i; // 错误:不能对register变量取地址 return 0; }
-
总结与思考
1)内容总结
-
核心知识点:
-
变量命名规范与存储原理(大小由类型决定,占据实际内存)。
-
auto
和register
存储类型的特性(作用域、生命周期、使用场景)。
-
-
关键区别:
-
auto
:常规局部变量,自动内存管理,作用域限于代码块。 -
register
:建议放入寄存器的优化变量,适用于高频访问的小型数据,不可取地址。
-
-
实践要点:
-
注意变量作用域和生命周期,避免跨作用域访问错误。
-
合理使用
register
优化关键变量(如循环计数器),但避免过度使用。
-
知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
变量基础概念 | 变量是程序中的存储空间抽象,占据内存空间,大小由数据类型决定 | 变量名规则:字母 / 数字 / 下划线组成,不能以数字开头;与关键字重名错误 | ⭐⭐ |
auto 存储类型 | 默认存储类型,局部变量,作用域限定在代码块内 | 未初始化时值为随机数;作用域外无法访问(编译报错) | ⭐⭐⭐ |
register 存储类型 | 建议编译器将变量存储在 CPU 寄存器中,无法取地址 | 仅适用于频繁访问的小型数据(≤4 字节);实际可能仍存内存(编译器可忽略建议) | ⭐⭐⭐⭐ |
宏定义应用 | 通过 #define 定义常量(如水分子质量 3.0e-23 ),提高代码可读性 |
宏替换是预处理阶段完成,无类型检查(可能引发类型不匹配问题) | ⭐⭐ |
科学计数法表示 | 使用浮点数指数形式(如 3.0e-23 )处理极小 / 极大值 |
输出时用 %e 或 %g 格式;注意浮点数精度损失问题 |
⭐⭐ |
变量作用域 | 演示 if 代码块内变量在外部不可访问 |
块作用域({} 内)与函数作用域的区别;局部变量与全局变量的作用域差异 |
⭐⭐⭐ |
数据类型选择 | 示例中使用 float 处理可能的大数值结果 |
浮点型(精度有限)与整型(无小数)的精度取舍;根据数值范围选择合适类型 |
Day02→嵌入式开发变量(二)
static 存储类型
定义与特性
-
定义 :
static
变量称为静态存储类型变量,可在函数体内(局部)或函数体外(全局)声明。 -
默认值 :未初始化时默认值为
0
(与全局变量一致,区别于auto
变量的随机值)。 -
作用范围:
-
修饰局部变量:延长生命周期至程序结束,但作用域仍限于原代码块。
-
修饰全局变量:限制作用域为当前文件(其他文件无法访问)。
-
1)static 局部变量示例
代码对比
// 普通局部变量(auto)
#include <stdio.h>
void func() {
int a = 0; // 每次调用重新初始化
a++;
printf("%d ", a);
}
int main() {
for (int i = 0; i < 4; i++) {
func(); // 输出:1 1 1 1
}
return 0;
}
// static局部变量
#include <stdio.h>
void func() {
static int a = 0; // 仅初始化一次,保留上次值
a++;
printf("%d ", a);
}
int main() {
for (int i = 0; i < 4; i++) {
func(); // 输出:1 2 3 4
}
return 0;
}
关键现象
-
未显式初始化时,
static
变量默认从0
开始(如上述示例输出1 2 3 4
)。 -
显式初始化后,从初始值累加(如
static int a = 5
,则输出6 7 8 9
)。
2)static 局部变量的特点
-
存储方式 :存于全局数据区(固定地址),而非栈内存(区别于
auto
变量)。 -
生命周期:从程序启动到结束(持续存在,不随代码块销毁)。
-
调用特性:
-
仅在第一次进入作用域时初始化,后续调用不再重新声明。
-
保留上次调用的结果(如累加器、计数器场景)。
-
-
与 auto 的区别:
特性 auto 局部变量 static 局部变量 默认值 随机值 0 存储位置 栈内存 全局数据区 生命周期 随代码块销毁 程序全程存在 初始化时机 每次进入作用域 仅第一次进入作用域
extern 存储类型
定义与使用条件
-
定义 :
extern
称为外部引用类型,用于引用其他文件中定义的全局变量(声明而非定义)。 -
使用条件:
-
被引用变量必须是全局变量(在函数体外定义)。
-
编译时需将多个文件联合编译(否则无法找到变量定义)。
-
-
语法格式 :
extern 数据类型 变量名;
(如extern int global_num;
)。
1)extern 全局变量示例
典型错误
-
未声明
extern
直接使用:编译报错undeclared identifier
。 -
未联合编译:即使声明
extern
,链接时仍报错undefined reference
。
正确用法
-
文件 1(定义全局变量) :
file1.c
int global_a = 100; // 全局变量定义(仅一次)
-
文件 2(引用全局变量) :
file2.c
#include <stdio.h> extern int global_a; // 声明外部变量(非定义) int main() { printf("global_a = %d\n", global_a); // 使用外部变量 return 0; }
-
联合编译:
gcc file1.c file2.c -o main # 链接两个文件 ./main # 输出:global_a = 100
2)总结与思考
-
static 修饰全局变量的效果 :将全局变量的作用域限制为当前文件(其他文件即使使用
extern
也无法访问),实现 "文件级封装"。 -
extern 与 static 的关系:
-
extern
可引用普通全局变量(未被static
修饰)。 -
extern
无法引用static 全局变量(受限于作用域)。
-
-
四种存储类型对比:
存储类型 核心特性 典型应用场景 auto 局部变量,栈内存,自动销毁 函数内临时变量 register 建议存寄存器,无地址,加速访问 循环计数器、高频访问变量 static 局部:延长生命周期;全局:限制作用域 累加器、文件内部全局变量 extern 跨文件引用全局变量,声明而非定义 多文件共享全局数据
知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 |
---|---|---|---|
static 修饰局部变量 | 1. 默认初始化为 0;2. 生命周期延长至程序结束;3. 作用域不变 | 与 auto 的区别:默认值(0 vs 随机)、生命周期(程序全程 vs 代码块内) | ⭐⭐⭐ |
static 修饰全局变量 | 限制全局变量仅在当前文件可见,禁止跨文件访问 | 与普通全局变量的对比:普通可跨文件访问,static 仅当前文件可见 | ⭐⭐ |
extern 关键字 | 声明外部全局变量,需联合编译多个文件 | 常见错误:未声明 extern、未联合编译;与 static 全局变量的冲突 | ⭐⭐⭐⭐ |
存储类型对比 | auto(栈)、static(全局区)、extern(跨文件)、register(寄存器) | 生命周期维度:auto < static = extern;作用域维度:static 全局 < 普通全局 | ⭐⭐⭐⭐ |
实验验证案例 | static 局部变量累加、多文件 extern 引用、static 全局变量限制访问 | static 局部变量的 "初始化仅一次" 特性;extern 的 "声明而非定义" 本质 |
Day02→运算符(1)
算术运算符
1)C 语言提供的算术运算符
-
运算符类型 :包括
+
(加)、-
(减)、*
(乘)、/
(除)、%
(取余)、++
(自增)、--
(自减)。 -
书写规范:
-
乘法符号:编程中使用
*
代替数学中的×
。 -
除法符号:使用左斜杠
/
代替数学中的÷
。 -
运算符间距:建议运算符两边加空格(如
a + b
),提高代码可读性。
-
-
特殊限制:
-
取余运算:仅适用于整型数据,
float
/double
等浮点类型不能进行取余运算。 -
双目运算符:加减乘除取余都需要两个运算量(如
a + b
)。 -
单目运算符:自增自减为单目运算符(如
i++
、--j
)。
-
例题:整数加减乘除运算
-
示例变量 :设
a = 10
,b = 3
。 -
运算结果:
-
加法:
a + b = 13
-
减法:
a - b = 7
-
乘法:
a * b = 30
-
除法:
a / b = 3
(整数除法取商,舍弃小数) -
取余:
a % b = 1
(取余数)
-
-
输出格式 :使用
%d
格式化输出整型结果(如printf("%d", a + b)
)。
例题:double 类型运算
-
示例变量 :设
x = 10.0
,y = 3.0
。 -
运算特点:
-
浮点除法:
x / y = 3.333333
(保留小数部分)。 -
取余限制:浮点数不能使用
%
运算符,会导致编译错误(如x % y
报错)。
-
-
输出格式 :使用
%f
或%lf
格式化输出浮点结果(如printf("%.2f", x / y)
)。 -
错误处理:需要注释掉浮点取余的代码才能通过编译。
关系运算符
1)C 语言的关系运算符
-
运算符类型 :包括
>
(大于)、>=
(大于等于)、<
(小于)、<=
(小于等于)、==
(等于)、!=
(不等于)。 -
书写规范:
-
等于判断:必须使用双等号
==
,与赋值运算符=
严格区分(如a == 5
是判断,a = 5
是赋值)。 -
不等判断:使用
!=
组合表示(如a != 0
表示 "a 不等于 0")。
-
-
运算结果 :返回逻辑值(
1
表示真,0
表示假)。
例题:关系运算符运算
-
示例变量 :设
a = 5
,b = 10
。 -
运算分析:
-
a > b
→5 > 10
→0
(假) -
a >= b
→5 >= 10
→0
(假) -
a < b
→5 < 10
→1
(真) -
a <= b
→5 <= 10
→1
(真) -
a == b
→5 == 10
→0
(假) -
a != b
→5 != 10
→1
(真)
-
-
应用场景 :主要用于条件判断语句(如
if
)和循环条件(如while
)。
逻辑运算符
1)逻辑非 !
运算符的运算律
-
基本运算 :包含逻辑反(非
!
)、逻辑与(&&
)、逻辑或(||
)三种运算符。 -
运算规律:
- 逻辑非:对运算量取反,
真
变假
,假
变真
。
- 逻辑非:对运算量取反,
-
运算示例:
-
!a
:判断a
不等于0
(若a
非 0,则!a
为 0;若a
为 0,则!a
为 1)。 -
a > 0 && a < 100
:判断a
在 0 到 100 之间。 -
a < 10 || b < 10
:判断a
或b
小于 10。
-
例题:逻辑非运算符使用
-
运算规则:
-
当运算量为
真
(非 0)时,结果为0
(假)。 -
当运算量为
假
(0)时,结果为1
(真)。
-
-
代码示例
int a = 5; printf("%d\n", !a); // 输出0(a非0,!a为假) int b = 0; printf("%d\n", !b); // 输出1(b为0,!b为真)
-
注意事项:
- 通常会对整个表达式加括号来明确优先级,例如:
!(a > b)
是对a > b
整体取反。
- 通常会对整个表达式加括号来明确优先级,例如:
2)逻辑与 &&
运算符的运算规律
-
运算规则:
-
只有左右运算量都为
真
(非 0)时,结果才为1
(真)。 -
其他情况结果均为
0
(假)。
-
-
短路特性:
- 从左到右依次处理,当左运算量为
假
(0)时,右运算量不执行(逢 0 截止)。
- 从左到右依次处理,当左运算量为
-
示例分析:
int a = 0, b = 5; int res = (a != 0) && (b++); // 左为假,右不执行,b仍为5 printf("%d %d\n", res, b); // 输出0 5
例题:if 条件语句逻辑与运算
-
题目解析:
-
当
(a > 0) && (b > 0)
成立时,才会执行printf
。 -
若
a = -1
,则整个表达式为假,printf
不会执行。
-
-
易错点:
-
注意短路特性可能导致某些表达式不被执行(如上述示例中
b++
未执行)。 -
变量初始化和使用顺序要明确。
-
3)逻辑或 ||
运算符的运算规律
-
运算规则:
-
只要有一个运算量为
真
(非 0),结果就为1
(真)。 -
只有两个都为
假
(0)时,结果才为0
(假)。
-
-
短路特性:
- 从左到右依次处理,当左运算量为
真
(非 0)时,右运算量不执行(逢 1 截止)。
- 从左到右依次处理,当左运算量为
例题:逻辑或运算符使用
-
题目解析:
int a = 5, b = 10; int res = (a > 0) || (b++); // 左为真,右不执行,b仍为10 printf("%d %d\n", res, b); // 输出1 10
-
注意事项:
-
与逻辑与相反,逻辑或是 "见 1 就停"。
-
注意区分
||
(逻辑或)和|
(位或)运算符的区别。
-
运算符总结
1. 算术运算符
-
基本运算 :包含加减乘除(
+
、-
、*
、/
)和取模运算(%
)。 -
浮点型注意 :对
float
/double
类型无需特殊处理,直接使用基本运算符即可,但不支持%
。
2. 关系运算符
-
比较运算 :包含小于(
<
)、小于等于(<=
)、大于(>
)、大于等于(>=
)、相等(==
)、不等(!=
)等。 -
结果特性:返回布尔值(0 或 1),用于条件判断。
3. 逻辑运算符
-
短路特性 :逻辑与(
&&
)是 "逢零截止",逻辑或(||
)是 "逢一截止"。 -
执行特点:部分表达式可能因短路特性而不会被执行。
-
使用注意:
-
逻辑与:当第一个操作数为假时,第二个操作数不会执行。
-
逻辑或:当第一个操作数为真时,第二个操作数不会执行。
-
4. 例题:逻辑运算求值
-
题目解析
(设
x = 5
,
y = 18
):
-
示例 1:
x > 0 && y < 20
→ 结果为 1(x=5
满足,y=18
满足)。 -
示例 2:
x > 0 && y < 10
→ 结果为 0(y=18
不满足)。 -
示例 3:
x < 0 && y < 20
→ 结果为 0(x=5
不满足)。 -
示例 4:
x > 0 || y < 10
→ 结果为 1(x=5
满足第一个条件)。 -
示例 5:
x < 0 || y < 20
→ 结果为 1(y=18
满足第二个条件)。
-
知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 | ||
---|---|---|---|---|---|
算术运算符 | 加减乘除、取余(% )操作;float /double 不支持取余运算 |
取余运算仅限整数;乘法用 * 代替 × ,除法用 / 代替 ÷ |
⭐⭐ | ||
关系运算符 | 大小比较(> , >= , < , <= )、相等(== )与不等(!= ) |
双等号 == 与赋值 = 的区别;表达式结果为 0(假)或 1(真) |
⭐⭐ | ||
逻辑运算符 | 逻辑与(&& )、逻辑或(` |
)、逻辑非( !`) |
短路特性:**逻辑与:逢 0 截止,**逻辑或:逢 1 截止;未执行的表达式可能影响变量值 | ⭐⭐⭐ | |
运算符优先级与规范 | 空格规范(如 a + b )、括号明确优先级 |
++ /-- 的前后置差异;复杂表达式建议用括号分组 |
⭐⭐ | ||
数据类型与运算限制 | int 支持完整算术运算;float /double 禁用取余 |
float 除法精度问题;混合类型运算的隐式转换 |
⭐⭐⭐ | ||
代码实践示例 | 演示整形 / 浮点运算差异;逻辑运算短路验证(如 b++ 未执行案例) |
if 条件中的副作用(如 |
Day02→运算符(2)
位运算符基础
1)C 语言的位运算符
-
运算基础:位运算以二进制为基础,需熟练掌握进制转换和原码 / 反码 / 补码知识。
-
效率优势:比算术运算符效率更高,在嵌入式开发中常用于精准控制硬件寄存器(如设置特定引脚电平)。
位逻辑反运算符(~)
-
符号表示 :
~
(单目运算符)。 -
运算规则:按位取反,0 变 1,1 变 0。
-
无符号数处理:直接对全部位取反,不考虑符号位(因无符号数无符号位)。
-
例题:字符取反计算
-
示例分析 : 定义
unsigned char x = 0x17
(二进制00010111
),取反后~x
得到11101000
(十六进制0xE8
)。 -
验证方法 :将十六进制每位转 4 位二进制后整体取反(如
0x17
拆分为0001
和0111
,取反后为1110
和1000
,组合为0xE8
)。 -
打印技巧 :使用
printf("%#x", y)
可自动添加0x
前缀(如输出0xe8
)。
-
位逻辑与运算符(&)
-
符号表示 :
&
(双目运算符)。 -
运算规则:两位都为 1 时结果为 1,否则为 0(即 "全 1 则 1,有 0 则 0")。
-
清零技巧 :用 0 与任何位相与可实现该位清零(如
a & 0xFE
可清零最低位)。 -
应用场景:嵌入式开发中精准控制寄存器特定位(如读取某引脚状态时屏蔽其他位)。
-
例题:字符与运算
-
示例分析 : 设
a = 056
(八进制,二进制01010110
),b = 0xac
(十六进制,二进制10101100
),则a & b
结果为00000100
(十六进制0x04
)。 -
验证要点:按位比对,只有第 3 位(从 0 开始计数)同时为 1,故结果对应位为 1。
-
位逻辑或运算符(|)
-
符号表示 :
|
(双目运算符)。 -
运算规则:两位有 1 时结果为 1,全 0 时才为 0(即 "有 1 则 1,全 0 则 0")。
-
置位技巧 :用 1 或任何位可实现该位置 1(如
a | 0x01
可将最低位置 1)。 -
例题:字符或运算
-
示例分析 : 设
a = 036
(八进制,二进制00111110
),b = 0x89
(十六进制,二进制10001001
),则a | b
结果为10111111
(十六进制0xBF
)。 -
计算技巧:逐位观察,只要两位中至少有 1,则结果位为 1。
-
位逻辑异或运算符(^)
-
符号表示 :
^
(双目运算符)。 -
运算规则:两位相异为 1,相同为 0(即 "同 0 异 1")。
-
特殊性质:
-
一个数异或自己结果为 0(
a ^ a = 0
)。 -
一个数异或 0 保持原值(
a ^ 0 = a
)。
-
-
例题:字符异或运算
-
示例分析 : 设
a = 75
(十进制,二进制01001011
),b = 0173
(八进制,二进制01111011
),则a ^ b
结果为00110000
(十六进制0x30
或八进制060
)。 -
记忆口诀:"同 0 异 1" 可快速判断异或结果。
-
2)位移位运算
移位运算的一般形式
-
基本语法 :
<运算量> <运算符> <整型表达式>
(如a << 3
,表示a
左移 3 位)。 -
运算符类型:
-
左移
<<
:高位丢弃,低位补 0。 -
右移
>>
:低位丢弃,高位补符号位(有符号数)或 0(无符号数)。
-
-
注意事项 :运算量和表达式都必须是整型数据类型(
char
、int
、long
等)。
位运算与移位操作
1. 十六进制与二进制转换
-
转换方法 :十六进制的每一位对应 4 位二进制(如
0x4
对应0100
,0xE
对应1110
,0xF
对应1111
)。 -
补位规则 :当二进制位数不足时,在前面补零使其达到数据类型要求的位数(如
unsigned char
为 8 位,0x4
补位后为00000100
)。
2. 左移运算原理
-
操作定义 :
a << 3
表示将变量a
的二进制位左移 3 位,右边空出的位补 0。 -
位移特性:
-
左移一位相当于乘以 2,左移 n 位相当于乘以
2ⁿ
(无溢出情况下)。 -
移出的高位直接丢弃,低位补零。
-
对于无符号数(如
unsigned char
),最高位移出不会产生符号问题(因无符号位)。
-
-
实例演示 :
0xE4
(二进制11100100
)左移 3 位后,高位 3 位丢弃,低位补 3 个 0,得到00100000
(十六进制0x20
)。
3. 例题:验证左移运算
-
题目解析:
代码实现:
#include <stdio.h> int main() { unsigned char x = 0xe4; // 二进制 11100100 unsigned char y = x << 3; printf("y = %#x\n", y); // 输出 y = 0x20 return 0; }
-
运行结果 :验证得到
y = 0x20
,与理论计算结果一致。 -
易错点:需注意数据类型是否为无符号数,有符号数的左移可能因符号位变化导致结果异常(如溢出后变为负数)。
4. 移位运算的数学本质
-
数学规律:
-
左移 1 位相当于乘以 2(
a << 1 = a * 2
)。 -
左移 2 位相当于乘以 4(
a << 2 = a * 4
)。 -
左移 3 位相当于乘以 8(
a << 3 = a * 8
)。
-
-
二进制演示
(以
0x04
为例):
-
初始值:
00000100
(4)。 -
左移 1 位:
00001000
(8 = 4 * 2)。 -
左移 2 位:
00010000
(16 = 4 * 4)。 -
左移 3 位:
00100000
(32 = 4 * 8)。
-
-
记忆口诀:"左移乘二,右移除二",可快速计算移位结果(无溢出时)。
知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 | |||
---|---|---|---|---|---|---|
位运算符基础 | 位运算以二进制为基础,与逻辑运算的区别;无符号数处理规则 | 进制转换(16 进制 / 8 进制 / 二进制);取反操作对无符号数的影响 | ⭐⭐ | |||
位运算类型 | 逻辑与(& )、逻辑或(` |
)、取反( ~)、异或( ^`)的运算规则及示例 |
异或运算 "同 0 异 1" 特性;与 / 或运算的清零 / 置位技巧 | ⭐⭐⭐ | ||
移位运算 | 左移(<< )与右移(>> )操作;左移等价于乘 2 的幂次方 |
移位后补位规则(左移补 0,右移区分有符号 / 无符号);无符号数移位与符号位问题 | ⭐⭐ | |||
位运算应用场景 | 提高程序效率(优于加减乘除);嵌入式开发中寄存器精准控制 | 硬件寄存器操作中的位掩码设计(如用 & 读取特定位,用 ` |
` 设置特定位) | ⭐⭐⭐⭐ | ||
实践验证方法 | 通过代码示例验证位运算结果,强调手动计算与程序输出对比 | 16 进制 / 二进制转换的准确性;移位运算中的溢出问题 | ⭐⭐ | |||
易错点总结 | 混淆逻辑运算符(&& /` |
Day02→运算符(3)
位运算的核心应用与例题解析
1. 思考
-
基本运算符 :包括位逻辑反(
~
)、位逻辑与(&
)、位逻辑或(|
)、位逻辑异或(^
)、位逻辑移位(<<
和>>
)。 -
核心应用:通过位运算可以精准控制数据的特定位(如设置为 1、清零、翻转等),在硬件寄存器操作中尤为重要(例如控制 GPIO 引脚电平、配置外设功能等)。
2. 例题:无符号数置 1
-
实现原理 : 利用位逻辑或(
|
) 的特性:1 与任何位进行或运算结果必为 1,0 与任何位进行或运算保持原值。通过构造 "目标位为 1,其余位为 0" 的掩码,与原数进行或运算,即可将目标位置 1。 -
掩码构造 : 将 1 左移
(y-1)
位生成仅目标位为 1 的掩码(如y=2
时,1 << 1 = 00000010
,掩码为0x02
)。 -
代码实现:
#include <stdio.h> int main() { unsigned char x = 0x04; // 二进制 0100 int y = 2; // 目标位(从1开始计数) x = x | (1 << (y - 1)); // 第2位置1,掩码为 00000010 printf("0x%x\n", x); // 输出 0x6(二进制 0110) return 0; }
-
验证过程:0x4(0100)的第 2 位置 1 后变为 0x6(0110),符合预期。
3. 例题:无符号数清 0
-
实现原理 : 利用位逻辑与(
&
) 的特性:0 与任何位进行与运算结果必为 0,1 与任何位进行与运算保持原值。通过构造 "目标位为 0,其余位为 1" 的掩码,与原数进行与运算,即可将目标位清零。 -
掩码构造 : 先构造目标位为 1 的掩码(
1 << (y-1)
),再对其取反(~
),得到目标位为 0、其余位为 1 的掩码(如y=3
时,~(1 << 2) = ~00000100 = 11111011
)。 -
代码实现:
#include <stdio.h> int main() { unsigned char x = 0x14; // 二进制 00010100 int y = 3; // 目标位(从1开始计数) x = x & ~(1 << (y - 1)); // 第3位清零,掩码为 11111011 printf("0x%x\n", x); // 输出 0x10(二进制 00010000) return 0; }
-
验证过程:0x14(00010100)的第 3 位(从右数)清零后变为 0x10(00010000),目标位成功清零。
4. 例题:十进制转十六进制(位运算优化)
-
数学原理:
十六进制的每位对应 4 位二进制,因此可用位运算替代除法和取余:
-
取整替代:
a / 16
等价于a >> 4
(右移 4 位,丢弃低 4 位,相当于除以 16)。 -
取余替代:
a % 16
等价于a & 0xF
(与 0xF(二进制 1111)运算,保留低 4 位,即余数)。
-
-
代码对比:
#include <stdio.h> int main() { int a = 65; // 常规方法 int quotient1 = a / 16; // 商为4 int remainder1 = a % 16; // 余为1 // 位运算方法 int quotient2 = a >> 4; // 右移4位,商为4 int remainder2 = a & 0xF; // 与0xF,余为1 printf("常规:商%d 余%d\n", quotient1, remainder1); printf("位运算:商%d 余%d\n", quotient2, remainder2); return 0; }
-
验证过程:65(十六进制 0x41)转换后商为 4、余为 1,两种方法结果一致,位运算更高效。
位运算的应用价值与学习建议
应用价值
位运算在底层开发(如嵌入式、驱动开发)中具有高效 (直接操作二进制,无需复杂运算)、精准(可单独控制某一位)的优势,特别适合硬件寄存器操作(如配置外设、读写状态位等)。
学习建议
-
反复练习掩码构造 (如何生成目标位的掩码)和位操作技巧(置 1、清零、翻转、提取位等)。
-
掌握位运算与常规运算的等价转换(如右移替代除法、与运算替代取余),理解其效率优势。
-
结合硬件场景练习(如模拟寄存器配置),加深对 "位级控制" 的理解。
知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 | |
---|---|---|---|---|
位运算基础 | 逻辑与(& )、逻辑或(` |
)、异或( ^)、位移( <</ >>`)的操作规则 |
位运算与逻辑运算的区别;移位方向与结果的关系 | ⭐⭐ |
位操作应用 | 无符号数特定位操作(置 1 / 清零);利用掩码实现精准控制 | 或运算(` | )置1与与运算( &`)清零的区别;掩码构造的逻辑 |
⭐⭐⭐ |
置 1 操作实现 | `x = x | (1 << (y-1))`(如 0x4 第 2 位置 1→0x6) | 左移位数与目标位的对应关系(y-1 的计算逻辑) |
⭐⭐ |
清零操作实现 | x = x & ~(1 << (y-1)) (如 0x14 第 3 位清零→0x10) |
取反操作(~ )的优先级;掩码取反后的位分布 |
⭐⭐⭐ | |
进制转换 | 十进制转十六进制:右移 4 位替代 ÷16,与 0xF 替代 %16 | 位运算替代数学运算的原理;0xF 的作用(保留低 4 位) | ⭐⭐⭐⭐ | |
硬件寄存器控制 | 位运算精准控制硬件寄存器特定位(如配置 GPIO、外设功能) | 实际应用中掩码的设计(如何覆盖目标位);多比特位同时操作的技巧 |
Day02→运算符(4)
赋值运算符
1)基本赋值运算符
-
基本形式 :
=
,如int a = 5;
-
执行特点:将右值表达式的计算结果赋给左值变量(左值必须是可修改的变量,不能是常量或表达式)。
-
常见误区 :编程中的
=
表示赋值,而非数学中的 "相等" 关系(判断相等用==
)。
2)赋值复合运算符
-
简化原理 :
a += b
等价于a = a + b
,其他运算符同理。 -
运算符类型:
-
算术类:
+=
、-=
、*=
、/=
、%=
-
位运算类:
&=
、|=
、^=
、<<=
、>>=
-
-
使用优势:简化代码书写,减少变量重复书写,部分编译器可优化执行效率。
例题:赋值复合运算符应用
#include <stdio.h>
int main() {
int sum = 0, count = 1;
while (count++ < 20) { // 先判断后自增,累加到20
sum += count; // 等价于 sum = sum + count
}
printf("sum = %d\n", sum); // 输出:210(1到20的累加和)
return 0;
}
-
代码解析:
-
sum += count
简化了累加操作,循环变量count
初始化为 1,通过自增控制循环次数。 -
计算结果为 1 到 20 的累加和(210),验证了复合运算符的实用性。
-
特殊运算符
1)条件运算符(三目运算符)
-
语法结构 :
表达式1 ? 表达式2 : 表达式3
-
执行逻辑:
-
先计算表达式 1;
-
若为真(非 0),整个表达式取表达式 2 的值;
-
若为假(0),取表达式 3 的值。
-
-
等价关系 :相当于简化的
if-else
结构(如a > b ? a : b
等价于if(a > b) return a; else return b;
)。
例题:三目运算符应用
#include <stdio.h>
int main() {
int score = 82;
// 成绩分级:≥101→100,90-100→90,<90→60
int res = (score >= 101) ? 100 : (score < 90 ? 60 : 90);
printf("res = %d\n", res); // 输出:60(82<90,取60)
return 0;
}
-
变量分析:
-
score = 82
,score >= 101
不成立 → 进入第二个三目运算; -
score < 90
成立 → 取 60,最终结果为 60。
-
例题:三目运算符与自增结合
#include <stdio.h>
int main() {
int x = 70, y;
// 版本1:先比较后自增(x++ > 80 中x先取值70,再自增为71)
y = (x++ > 80) ? 100 : 0;
printf("版本1:x=%d, y=%d\n", x, y); // 输出:71, 0
x = 70; // 重置x
// 版本2:先自增后比较(++x > 80 中x先自增为71,再比较)
y = (++x > 80) ? 100 : 81;
printf("版本2:x=%d, y=%d\n", x, y); // 输出:71, 81
return 0;
}
- 关键点 :区分
x++
(先取值后自增)和++x
(先自增后取值)的执行时机,直接影响结果。
2)逗号运算符
-
语法特点 :用括号包含多个表达式,以逗号分隔(如
(表达式1, 表达式2, ..., 表达式n)
)。 -
执行规则:
-
从左到右依次执行各个表达式;
-
整个表达式的值为最后一个表达式的值。
-
-
典型应用:
-
多变量初始化:
int a = 1, b = 2, c = 3;
-
复合运算:
for (i=0, j=10; i<j; i++, j--)
。
-
例题:逗号运算符应用
#include <stdio.h>
int main() {
float x = 5.5, y = 1.8;
float res = (x *= 3, y += 1.0, x + y); // 逗号表达式
// 执行步骤:
// 1. x *= 3 → x = 16.5
// 2. y += 1.0 → y = 2.8
// 3. 取最后一个表达式 x + y → 19.3
printf("res = %.1f\n", res); // 输出:19.3(可能有浮点精度误差)
return 0;
}
3)sizeof 运算符
-
功能作用:计算数据类型或变量所占内存的字节数。
-
使用形式:
-
类型:
sizeof(数据类型)
(如sizeof(int)
); -
变量:
sizeof(变量名)
或sizeof 变量名
(如sizeof(a)
或sizeof a
)。
-
-
32 位系统典型值:
int
:4 字节;long
:4 字节;long long
:8 字节;char
:1 字节。
-
注意事项 :结果由数据类型决定,与变量的具体值无关(如
int a = 0; sizeof(a)
仍为 4 字节)。
运算符的优先级
1)优先级表(核心规则)
-
优先级规则:表中优先级从上到下依次降低(数值越小优先级越高,如 1 为最高,15 为最低)。
-
常见运算符优先级(从高到低):
-
括号(
()
)、方括号([]
); -
单目运算符(
!
、~
、++
、--
、sizeof
); -
算术运算符(
*
、/
、%
高于+
、-
); -
位运算符(
<<
、>>
高于&
、^
、|
); -
关系运算符(
>
、<
、>=
、<=
高于==
、!=
); -
逻辑运算符(
&&
高于||
); -
三目运算符(
?:
); -
赋值运算符(
=
、+=
、-=
等); -
逗号运算符(
,
)。
-
-
易混淆点:
-
赋值运算符(
=
)优先级低于逻辑判断运算符(==
); -
逻辑与(
&&
)优先级高于逻辑或(||
)。
-
-
记忆技巧:
-
括号优先级最高,可强制改变运算顺序;
-
复杂表达式建议用括号明确顺序(如
(a + b) * c
比a + b * c
更清晰)。
-
例题:运算符优先级应用
#include <stdio.h>
int main() {
int x = 1, y = 0, z = 0;
// 逻辑与(&&)优先级高于逻辑或(||)
int result = x++ && y++ || ++z;
// 执行顺序:
// 1. x++ && y++ → x先取值1(后自增为2),y先取值0(后自增为1),逻辑与结果为0
// 2. 0 || ++z → 计算++z(z变为1),逻辑或结果为1
printf("result = %d, x=%d, y=%d, z=%d\n", result, x, y, z);
// 输出:result=1, x=2, y=1, z=1
return 0;
}
知识小结
知识点 | 核心内容 | 考试重点 / 易混淆点 | 难度系数 | ||||
---|---|---|---|---|---|---|---|
复合赋值运算符 | += 、-= 、*= 等简化写法(如 a += b 等价于 a = a + b ) |
自增 / 自减与复合运算的结合(如 count++ < 20 先判断后自增) |
⭐⭐ | ||||
三目运算符 | 表达式1 ? 表达式2 : 表达式3 ,等价于简化的 if-else |
执行顺序:先判断表达式 1,再选择表达式 2/3;与自增结合时的执行时机 | ⭐⭐⭐ | ||||
逗号运算符 | 多个表达式从左到右执行,结果为最后一个表达式的值 | 优先级最低,与赋值或逻辑运算结合时需加括号(如 (a=1, b=2, a+b) ) |
⭐⭐⭐⭐ | ||||
sizeof 运算符 | 计算变量或类型的内存字节数(如 sizeof(int) ) |
结果由类型决定,与变量值无关;区分 sizeof(a) 和 sizeof(a[0]) (数组场景) |
⭐ | ||||
运算符优先级 | 括号 > 算术 > 关系 > 逻辑 > 赋值 > 逗号 | == 优先级高于 = (如 if(x = y == 1) 易出错);逻辑与优先级高于逻辑或 |
⭐⭐⭐⭐ | ||||
短路特性 | 逻辑与(&& )逢 0 截止,逻辑或(` |
`)逢 1 截止 | 结合优先级分析短路条件(如 `x++ && y++ | z++` 中逻辑与优先执行) | ⭐⭐⭐ | ||
自增 / 自减陷阱 | x++ (先取值后自增)与 ++x (先自增后取值)的区别 |
复合表达式中影响结果(如三目运算符、逻辑判断中的自增时机) |