当前位置: 首页 > news >正文

暑期自学嵌入式——Day02(C语言阶段)

点关注不迷路哟。你的点赞、收藏,一键三连,是我持续更新的动力哟!!!

主页:

一位搞嵌入式的 genius-CSDN博客https://blog.csdn.net/m0_73589512?spm=1000.2115.3001.5343

目录

Day02→数据类型(上)

数据类型分类

基本数据类型

整形数据类型

字符型数据类型

实型数据类型

构造数据类型

特殊数据类型

布尔类型详解

基本概念

使用注意事项

预处理分析

知识小结

Day02→数据类型(下)

char类型

short 类型

short 类型与 unsigned short 类型的定义

short 类型与 int 类型的长度区别

示例:打印类型长度

short 类型值域的分析

int 类型

limits.h 头文件

头文件作用

文件位置与包含方式

主要宏定义内容

实际应用示例

使用建议

知识小结

Day02→常量

常量定义

基本数据类型常量

1)整型常量

2)浮点型常量

标识常量(宏定义)

1)使用规范

2)优势与局限

基本数据类型的常量(补充细节)

1)整型常量

2)浮点常量

3)指数常量

4)字符常量

5)字符串常量

应用案例(精选)

例题:字符常量运算

例题:宏定义的文本替换

知识小结

Day02→嵌入式开发变量(一)

作业讲解

变量的基础

1)变量的概念

2)变量的存储空间

变量的说明

变量的存储类型

1)auto

2)register

总结与思考

1)内容总结

知识小结

Day02→嵌入式开发变量(二)

static 存储类型

定义与特性

1)static 局部变量示例

代码对比

关键现象

2)static 局部变量的特点

extern 存储类型

定义与使用条件

1)extern 全局变量示例

典型错误

正确用法

2)总结与思考

知识小结

Day02→运算符(1)

算术运算符

1)C 语言提供的算术运算符

例题:整数加减乘除运算

例题:double 类型运算

关系运算符

1)C 语言的关系运算符

例题:关系运算符运算

逻辑运算符

1)逻辑非 ! 运算符的运算律

例题:逻辑非运算符使用

2)逻辑与 && 运算符的运算规律

例题:if 条件语句逻辑与运算

3)逻辑或 || 运算符的运算规律

例题:逻辑或运算符使用

运算符总结

1. 算术运算符

2. 关系运算符

3. 逻辑运算符

4. 例题:逻辑运算求值

知识小结

Day02→运算符(2)

位运算符基础

1)C 语言的位运算符

位逻辑反运算符(~)

位逻辑与运算符(&)

位逻辑或运算符(|)

位逻辑异或运算符(^)

2)位移位运算

移位运算的一般形式

位运算与移位操作

1. 十六进制与二进制转换

2. 左移运算原理

3. 例题:验证左移运算

4. 移位运算的数学本质

知识小结

Day02→运算符(3)

位运算的核心应用与例题解析

1. 思考

2. 例题:无符号数置 1

3. 例题:无符号数清 0

4. 例题:十进制转十六进制(位运算优化)

位运算的应用价值与学习建议

应用价值

学习建议

知识小结

Day02→运算符(4)

赋值运算符

1)基本赋值运算符

2)赋值复合运算符

例题:赋值复合运算符应用

特殊运算符

1)条件运算符(三目运算符)

例题:三目运算符应用

例题:三目运算符与自增结合

2)逗号运算符

例题:逗号运算符应用

3)sizeof 运算符

运算符的优先级

1)优先级表(核心规则)

例题:运算符优先级应用

知识小结


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~214748364732/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.143e53×10⁵)。

  • 科学计数法:应写作 1.23e41.23×10⁴)。

  • 默认类型:未加后缀默认为 double 类型(加 f 后缀为 float,如 3.14f)。

标识常量(宏定义)

  • 别称:也称为宏定义(#define)。

  • 作用:用标识符代表常量值,提高代码可读性。

  • 语法格式#define 标识符 常量值(如 #define PI 3.14159)。

  • 预处理特性:在编译前进行文本替换,不占用内存。

1)使用规范

  • 命名约定:通常使用全大写字母,如 #define MAX 100

  • 作用范围:从定义处开始到文件结束。

  • 类型安全:宏定义不进行类型检查,仅做纯文本替换。

2)优势与局限

  • 优势:

    • 便于集中修改常量值(如修改 PI 精度只需改定义处)。

    • 避免 “魔法数字”(直接写 3.14 等无意义数值),提高代码可维护性。

  • 局限:

    • 不占用内存空间(无存储地址,调试时无法查看值)。

    • 替换可能引发逻辑错误(如 #define TWO 1+1TWO*2 会被替换为 1+1*2=3,需加括号 #define TWO (1+1))。

基本数据类型的常量(补充细节)

1)整型常量

  • 进制表示细节:

    • 八进制前缀 0 和十六进制前缀 0x 不可省略(如 077 是八进制 630x77 是十六进制 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 = 553 - 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) = 9printf("%c %c %d\n", c1, c2, num);  // 输出:E B 9return 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)变量的概念

  • 命名规则:

    • 组成:字母、数字、下划线。

    • 限制:不能以数字开头,不能与关键字重名(如 intif 等)。

  • 本质:程序中的存储空间抽象,用于存放可变化的数据。

  • 地址特性:变量首地址可通过 & 运算符获取(如 &num 表示变量 num 的地址)。

2)变量的存储空间

  • 空间分配:

    • 大小由数据类型决定(如 int 占 4 字节,char 占 1 字节),与变量值无关。

    • 运行时实际占用内存空间,空间大小可通过 sizeof(变量名) 查看。

  • 地址特性:变量首地址可通过 & 运算符获取,用于指针操作等场景。

变量的说明

  • 标准形式<存储类型> <数据类型> <变量名> 示例:auto int age;static float score;

  • 存储类型autoregisterstaticextern(决定变量的存储方式和生命周期)。

  • 数据类型:基本类型(如 intfloat)或自定义类型(如结构体、数组)。

  • 默认情况:存储类型 auto 可省略(如 int a; 等价于 auto int a;)。

变量的存储类型

1)auto

  • 属性:局部变量,默认存储类型。

  • 作用范围:限定在函数体或复合语句块(如 iffor 块)内。

  • 生命周期:进入作用域时创建,离开作用域时自动销毁。

  • 默认值:未初始化时为随机值(需手动初始化后使用)。

  • 应用案例: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 可直接处理的类型(如 intchar),长度不超过整型(通常 4 字节)。

    • 操作限制:不能使用 & 取地址(寄存器无内存地址),无法通过指针访问。

    • 性能考量:过度使用可能导致寄存器资源紧张,反而降低性能,仅适用于高频访问的小型变量(如循环计数器)。

    • 示例

      #include <stdio.h>
      int main() {register int i;  // 建议将i存入寄存器(适合循环计数)for (i = 0; i < 1000000; i++) {// 频繁使用i,寄存器存储可加速循环}// &i;  // 错误:不能对register变量取地址return 0;
      }

总结与思考

1)内容总结

  • 核心知识点:

    • 变量命名规范与存储原理(大小由类型决定,占据实际内存)。

    • autoregister 存储类型的特性(作用域、生命周期、使用场景)。

  • 关键区别:

    • 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. 文件 1(定义全局变量)file1.c

    int global_a = 100;  // 全局变量定义(仅一次)
  2. 文件 2(引用全局变量)file2.c

    #include <stdio.h>
    extern int global_a;  // 声明外部变量(非定义)
    int main() {printf("global_a = %d\n", global_a);  // 使用外部变量return 0;
    }
  3. 联合编译

    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 = 10b = 3

  • 运算结果:

    • 加法:a + b = 13

    • 减法:a - b = 7

    • 乘法:a * b = 30

    • 除法:a / b = 3(整数除法取商,舍弃小数)

    • 取余:a % b = 1(取余数)

  • 输出格式:使用 %d 格式化输出整型结果(如 printf("%d", a + b))。

例题:double 类型运算

  • 示例变量:设 x = 10.0y = 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 = 5b = 10

  • 运算分析:

    • a > b5 > 100(假)

    • a >= b5 >= 100(假)

    • a < b5 < 101(真)

    • a <= b5 <= 101(真)

    • a == b5 == 100(假)

    • a != b5 != 101(真)

  • 应用场景:主要用于条件判断语句(如 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:判断 ab 小于 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 拆分为 00010111,取反后为 11101000,组合为 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(无符号数)。

  • 注意事项:运算量和表达式都必须是整型数据类型(charintlong 等)。

位运算与移位操作

1. 十六进制与二进制转换

  • 转换方法:十六进制的每一位对应 4 位二进制(如 0x4 对应 01000xE 对应 11100xF 对应 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;  // 二进制 11100100unsigned char y = x << 3;printf("y = %#x\n", y);  // 输出 y = 0x20return 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;  // 二进制 0100int y = 2;               // 目标位(从1开始计数)x = x | (1 << (y - 1));  // 第2位置1,掩码为 00000010printf("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;  // 二进制 00010100int y = 3;               // 目标位(从1开始计数)x = x & ~(1 << (y - 1)); // 第3位清零,掩码为 11111011printf("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;   // 商为4int remainder1 = a % 16;  // 余为1// 位运算方法int quotient2 = a >> 4;   // 右移4位,商为4int remainder2 = a & 0xF; // 与0xF,余为1printf("常规:商%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) {  // 先判断后自增,累加到20sum += 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. 先计算表达式 1;

    2. 若为真(非 0),整个表达式取表达式 2 的值;

    3. 若为假(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→60int res = (score >= 101) ? 100 : (score < 90 ? 60 : 90);printf("res = %d\n", res);  // 输出:60(82<90,取60)return 0;
}
  • 变量分析:

    • score = 82score >= 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, 0x = 70;  // 重置x// 版本2:先自增后比较(++x > 80 中x先自增为71,再比较)y = (++x > 80) ? 100 : 81;  printf("版本2:x=%d, y=%d\n", x, y);  // 输出:71, 81return 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.3printf("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 为最低)。

  • 常见运算符优先级(从高到低)

    1. 括号(())、方括号([]);

    2. 单目运算符(!~++--sizeof);

    3. 算术运算符(*/% 高于 +-);

    4. 位运算符(<<>> 高于 &^|);

    5. 关系运算符(><>=<= 高于 ==!=);

    6. 逻辑运算符(&& 高于 ||);

    7. 三目运算符(?:);

    8. 赋值运算符(=+=-= 等);

    9. 逗号运算符(,)。

  • 易混淆点

    • 赋值运算符(=)优先级低于逻辑判断运算符(==);

    • 逻辑与(&&)优先级高于逻辑或(||)。

  • 记忆技巧

    • 括号优先级最高,可强制改变运算顺序;

    • 复杂表达式建议用括号明确顺序(如 (a + b) * ca + 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),逻辑或结果为1printf("result = %d, x=%d, y=%d, z=%d\n", result, x, y, z);// 输出:result=1, x=2, y=1, z=1return 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(先自增后取值)的区别复合表达式中影响结果(如三目运算符、逻辑判断中的自增时机)
http://www.lryc.cn/news/588479.html

相关文章:

  • PyTorch张量(Tensor)创建的方式汇总详解和代码示例
  • 如何降低AIGC的查重率?精选六个AIGC降重让论文更出色
  • 《每日AI-人工智能-编程日报》--2025年7月14日
  • Android Studio C++/JNI/Kotlin 示例 三
  • git项目,有idea文件夹,怎么去掉
  • Mybatis(黑马)
  • 网络传输过程
  • 理解Linux文件系统:从物理存储到统一接口
  • 小波变换 | 离散小波变换
  • 学习笔记——农作物遥感识别与大范围农作物类别制图的若干关键问题
  • rsyslog简单应用
  • Linux中的系统日志(Rsyslog)
  • 算法训练营day17 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树
  • Linux —— A / 基础指令
  • 深入解析Hadoop YARN架构设计:从原理到实践
  • 019 进程控制 —— 进程程序替换
  • SpringMVC2
  • 力扣-138.随机链表的复制
  • 一分钟K线实时数据数据接口,逐笔明细数据接口,分时成交量数据接口,实时五档委托单数据接口,历史逐笔明细数据接口,历史分时成交量数据接口
  • 深入理解MyBatis延迟加载:原理、配置与实战优化
  • 美丽田园发布盈喜公告,预计净利增长超35%该咋看?
  • 现场设备无法向视频汇聚EasyCVR视频融合平台推流的原因排查与解决过程
  • CA-IS3082W 隔离485 收发器芯片可能存在硬件BUG
  • 第十五节:Vben Admin 最新 v5.0 (vben5) + Python Flask 快速入门 - vue前端 生产部署
  • Laravel 中 chunk 分页漏掉数据?深度解析原因与解决方案
  • Unity3D + VS2022连接雷电模拟器调试
  • 4、qt窗口(沉淀中)
  • iOS APP 上架流程:跨平台上架方案的协作实践记录
  • ConcurrentHashMap 原子操作详解:computeIfAbsent、computeIfPresent和putIfAbsent
  • C语言-数据输入与输出