【C 语言入门第 15 天】共用体、枚举、位运算核心知识点梳理
本文对应 C 语言学习第十五天的核心内容,系统梳理共用体(联合体)、枚举类型、位运算三大模块的基础语法、核心用法与高频易错点。
文章目录
- 一、共用体(联合体 union)
- 1. 核心本质
- 2. 定义与使用语法
- 3. 内存大小计算
- 4. 经典应用:判断系统大小端
- 5. 高频易错点
- 二、枚举类型 enum
- 1. 核心作用
- 2. 定义与默认取值
- 3. 手动赋值示例
- 4. 高频易错点
- 三、位运算
- 1. 位运算符总览
- 2. 三大经典位操作
- 3. 位移运算注意事项
- 4. 高频易错点
- 四、代码实战通用踩坑总结(结合作业易错点)
- 五、学习总结
一、共用体(联合体 union)
1. 核心本质
共用体(也叫联合体)是 C 语言的自定义数据类型,核心特征是所有成员共享同一块内存空间,这也是它和结构体最本质的区别:
- 结构体 struct:每个成员拥有独立的内存空间,互不影响,总大小为各成员大小之和(含内存对齐)
- 共用体 union:所有成员共用同一段起始地址的内存,同一时间只能有效存储一个成员的值
2. 定义与使用语法
// 定义共用体类型 union Data { int a; char b; float c; }; // 定义共用体变量 union Data data;3. 内存大小计算
共用体的总大小 = 占用内存最大的成员的大小,同时需要满足内存对齐规则。 以上面的union Data为例:
- int 占 4 字节,char 占 1 字节,float 占 4 字节
- 最大成员大小为 4 字节,因此该共用体变量占用 4 字节
4. 经典应用:判断系统大小端
利用共用体共享内存的特性,可以判断当前系统的字节存储序:
- 小端模式:内存低地址存放数据的低字节,高地址存放高字节
- 大端模式:内存低地址存放数据的高字节,高地址存放低字节
#include <stdio.h> int check_endian(void) { union { int a; char b; } test; test.a = 1; return test.b; // 小端返回1,大端返回0 } int main() { if (check_endian()) { printf("当前系统为小端模式\n"); } else { printf("当前系统为大端模式\n"); } return 0; }5. 高频易错点
- 不能同时给多个成员赋值:由于内存共享,修改一个成员会覆盖其他成员的值,同一时间只有一个成员有效。
- 混淆结构体与共用体:结构体总大小是各成员大小之和(含对齐),共用体取最大成员大小,切勿记混。
- 大小端概念记反:口诀辅助记忆 ——小端低低,大端低高(小端:低地址存低字节;大端:低地址存高字节)。
二、枚举类型 enum
1. 核心作用
枚举用于定义一组命名的整型常量,所有枚举常量的本质都是int类型,主要用来提高代码可读性,用有意义的单词代替魔法数字。
2. 定义与默认取值
enum weekday { MONDAY, // 默认值 0 TUESDAY, // 1 WEDNESDAY, // 2 THURSDAY, // 3 FRIDAY, // 4 SATURDAY, // 5 SUNDAY // 6 };取值规则:
- 第一个枚举常量默认值为 0
- 后续常量的值默认 = 前一个常量的值 + 1
- 支持手动指定某个常量的值,后续常量依旧在前一个基础上 +1
3. 手动赋值示例
enum weekday { MONDAY = 1, // 手动设为1 TUESDAY, // 2 WEDNESDAY, // 3 };4. 高频易错点
- 语法括号错误:枚举常量必须全部写在大括号内部,把常量写在枚举定义外部属于语法错误:
enum weekday { MONDAY, ... SATURDAY, }; SUNDAY, // ❌ 错误,写在了枚举大括号外部 - 枚举常量不能被赋值:枚举元素是常量,运行中不能写
MONDAY = 10;,编译会直接报错。 - 误以为是字符串类型:枚举常量本质是 int 整数,直接
printf("%s", MONDAY)是错误的,只能打印整数值。 - 同名冲突:同一作用域内,不同枚举的常量名不能重复。
三、位运算
位运算直接对二进制位进行操作,是 C 语言底层操作的核心,也是嵌入式开发、笔试面试的高频考点。
1. 位运算符总览
| 运算符 | 名称 | 运算规则 | |
|---|---|---|---|
& | 按位与 | 对应位全 1 才为 1,有 0 则为 0 | |
| ` | ` | 按位或 | 对应位有 1 则为 1,全 0 才为 0 |
^ | 按位异或 | 对应位不同为 1,相同为 0 | |
~ | 按位取反 | 0 变 1,1 变 0(单目运算符) | |
<< | 左移 | 二进制位左移,低位补 0 | |
>> | 右移 | 二进制位右移,无符号数补 0,有符号数补符号位 |
速记口诀:与 0 得 0,或 1 置 1,异或取反。
2. 三大经典位操作
(1)将变量的第 n 位置 1
num |= (1 << n);原理:将 1 左移 n 位,得到只有第 n 位为 1、其余位为 0 的数,再和原数按位或,即可把第 n 位置 1,且不影响其他位。
(2)将变量的第 n 位清 0
num &= ~(1 << n);原理:先将 1 左移 n 位,再按位取反得到只有第 n 位为 0、其余位为 1 的数,和原数按位与即可清零第 n 位。
(3)异或实现两数交换(无需临时变量)
a = a ^ b; b = a ^ b; a = a ^ b;原理:利用异或的性质 —— 一个数异或自身等于 0,异或 0 等于自身。
3. 位移运算注意事项
- 左移
<<:在不溢出的前提下,相当于数值乘以 2^n - 右移
>>:对于有符号数,多数编译器采用算术右移,高位补符号位;无符号数采用逻辑右移,高位补 0 - 位移位数不能为负数,也不能超过数据类型的总位数,否则结果属于未定义行为
4. 高频易错点
- 运算符优先级混淆:位运算符优先级低于算术运算符、关系运算符,复杂表达式建议加括号避免出错,比如
1 << n + 1会先算加法再移位,和预期逻辑不符。 - 按位取反~和逻辑非!混淆:
~是对每一位二进制逐位取反,!是对整体逻辑真假取反,二者完全不同。 - 异或交换的陷阱:如果两个变量指向同一块内存(比如自己和自己交换),执行后结果会变成 0,使用前要确保两个变量地址不同。
- 忽略符号位:对有符号负数进行右移操作时,要注意高位补的是 1,不是 0。
四、代码实战通用踩坑总结(结合作业易错点)
结合近期代码编写中的典型错误,整理通用避坑指南:
- 循环变量忘记更新导致死循环:手写反转、遍历函数时,while 循环内只写业务逻辑,忘记
start++、end--,导致循环条件永远成立,程序直接卡死。 - 边界条件遗漏:处理字符串、数组时,循环终止条件草率写成
< len,导致最后一个元素、最后一个单词没有被处理,要根据逻辑判断是否需要<= len。 - 整数除法丢失精度:两个 int 相除默认是整数除法,小数部分直接截断;求平均分、平均值时,必须先强制转换其中一个数为浮点型,再做除法。
- 输入函数踩坑:
scanf("%s")遇到空格就停止读取,无法读取带空格的整行字符串 - 计数器 / 累加器忘记清零:在多层循环中,处理每一个元素前,计数变量、求和变量必须重置为 0,否则会累积上一轮的结果,导致逻辑完全错误。
五、学习总结
- 共用体核心是内存共享,常用于大小端判断、节省内存的多类型数据存储场景。
- 枚举本质是整型常量集合,作用是提升代码可读性,注意语法规范和默认取值规则。
- 位运算直接操作二进制,是嵌入式开发、底层优化的基础,重点掌握置 1、清 0、交换三个经典操作,牢记运算符规则与优先级陷阱。