C 语言核心特性详解:typedef、共用体、位运算、位段、枚举类型、内存管理
这六个主题是 C 语言中非常重要且经常一起考察的“底层细节”部分,尤其在嵌入式开发、系统编程、算法面试、驱动开发等领域,几乎是必考内容。下面按清晰的结构逐一讲解,重点突出定义、用法、典型场景、常见陷阱和面试高频写法。
1. typedef(类型别名)
作用:给已有的类型起一个新的名字,提高代码可读性、移植性、一致性。
最常见的几种用法
// 1. 基本类型别名(最常用)
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
// 2. 结构体别名(最经典用法)
typedef struct {
int x;
int y;
} Point; // 以后可以直接写 Point p;
// 3. 指针类型别名(非常常见,尤其函数指针)
typedef int (*FuncPtr)(int, int); // FuncPtr 是一个指向函数的指针类型
// 4. 数组类型别名
typedef int IntArray[10]; // IntArray 等价于 int[10]
// 5. void* 的别名(常用于泛型编程)
typedef void* Handle;
面试高频写法对比
// 错误 / 不推荐
struct Point { int x, y; };
struct Point p; // 每次都要写 struct
// 推荐(现代 C 项目几乎都这么写)
typedef struct Point {
int x, y;
} Point;
Point p; // 简洁
常见陷阱typedef 只是起别名,不是创建新类型,所以下面两种是完全等价的:
typedef int* IntPtr;
IntPtr a, b; // a 和 b 都是 int*
int* a, b; // a 是 int*,b 是 int ← 完全不同!
2. 共用体(union)
核心思想:同一块内存被多个成员共用,同一时刻只能有一个成员有效。
语法
union Data {
int i; // 4 字节
float f; // 4 字节
char c; // 1 字节
double d; // 8 字节(union 整体大小取最大成员)
};
union Data d;
printf("sizeof(union Data) = %zu\n", sizeof(d)); // 通常输出 8
典型用途
- 节省内存(嵌入式最常见)
- 类型转换 / 内存解释(查看内存的多种解释)
- 实现类似 C++ 的 union 类型(C11 _Generic 之前)
经典面试题:用 union 判断大小端
int is_little_endian() {
union {
int a;
char b;
} u = {0x12345678};
return u.b == 0x78; // 小端:低字节在低地址
}
3. 位运算(Bit Operations)
C 语言位运算符是底层编程的利器,尤其在寄存器操作、标志位、算法优化中无处不在。
| 运算符 | 含义 | 用途示例 |
|---|---|---|
| & | 按位与 | 清零特定位、取交集 |
| | | 按位或 | 置位特定位、取并集 |
| ^ | 按位异或 | 翻转特定位、交换两个数(不用临时变量) |
| ~ | 按位取反 | 全部位翻转(注意符号位) |
| << | 左移 | 乘 2^n、快速置位 |
| >> | 右移 | 除 2^n(有符号右移保留符号位) |
高频技巧
// 1. 判断奇偶
if (n & 1) // 奇数
// 2. 交换两个数(不用临时变量)
a ^= b;
b ^= a;
a ^= b;
// 3. 取一个数的第 k 位(从 0 开始)
int bit = (n >> k) & 1;
// 4. 设置第 k 位为 1
n |= (1 << k);
// 5. 清零第 k 位
n &= ~(1 << k);
// 6. 翻转第 k 位
n ^= (1 << k);
// 7. 统计二进制 1 的个数(Brian Kernighan 算法)
int count = 0;
while (n) {
n &= (n - 1); // 每次清掉最低位的 1
count++;
}
4. 位段(Bit-field)
作用:在结构体中用位而不是字节来定义成员,极度节省内存,常用于硬件寄存器映射、协议字段定义。
语法
struct Flags {
unsigned int flag1 : 1; // 只占 1 bit
unsigned int flag2 : 1;
unsigned int mode : 3; // 占 3 bit,可表示 0~7
unsigned int value : 8; // 占 8 bit
// 总共 13 bit,通常被填充到 2 字节或 4 字节
};
struct Flags f = {0};
f.flag1 = 1; // 合法
f.mode = 5; // 合法
// f.value = 300; // 非法!越界编译器可能警告或截断
关键规则
- 位段只能是 int、signed int、unsigned int、_Bool(C99 后支持 _Bool)
- 位段成员不能取地址(& 操作非法)
- 位段的存储顺序依赖编译器实现(通常跟机器字节序一致,但不保证跨平台)
- 位段之间不能跨类型边界(除非显式指定 :0 填充)
典型应用:硬件寄存器映射
typedef struct {
uint32_t enable : 1;
uint32_t mode : 2;
uint32_t reserved : 13;
uint32_t irq_status : 8;
uint32_t reserved2 : 8;
} __attribute__((packed)) RegConfig;
5. 枚举类型(enum)
C 的枚举本质上是一组具名整数常量。
基本写法
enum Color {
RED = 0,
GREEN,
BLUE = 5, // 可以指定值
YELLOW
};
enum Color c = RED; // 合法
c = 10; // 合法(C 枚举是弱类型)
C11 引入的 enum class(强类型枚举)(推荐)
enum class Status : int {
OK = 0,
ERROR,
TIMEOUT
};
Status s = Status::OK; // 必须带 Status::
int x = static_cast<int>(s); // 显式转换
面试高频用法:状态机、错误码
typedef enum {
STATE_IDLE,
STATE_CONNECTING,
STATE_CONNECTED,
STATE_DISCONNECTING
} StateMachine;
6. 内存管理(malloc / calloc / realloc / free)
C 语言手动内存管理是其强大但也最容易出错的地方。
| 函数 | 作用 | 是否清零 | 常见用法场景 |
|---|---|---|---|
| malloc | 分配 size 字节内存 | 否 | 通用 |
| calloc | 分配 num × size 字节,并清零 | 是 | 初始化结构体、数组 |
| realloc | 调整已有内存块大小(可扩可缩) | — | 动态数组扩容 |
| free | 释放 malloc/calloc/realloc 分配的内存 | — | 必须成对使用 |
经典动态二维数组写法(最常考)
int** create_matrix(int rows, int cols) {
int** matrix = malloc(rows * sizeof(int*));
if (!matrix) return NULL;
for (int i = 0; i < rows; i++) {
matrix[i] = malloc(cols * sizeof(int));
if (!matrix[i]) {
// 清理已分配部分
for (int j = 0; j < i; j++) free(matrix[j]);
free(matrix);
return NULL;
}
// 可选:memset(matrix[i], 0, cols * sizeof(int));
}
return matrix;
}
void free_matrix(int** matrix, int rows) {
if (!matrix) return;
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
}
内存管理高频陷阱
- 释放后使用(use-after-free)
- 重复释放(double free)
- 释放非动态分配的内存(free 栈上变量)
- 忘记释放(内存泄漏)
- realloc 失败后原指针失效(要用临时变量接收)
现代推荐做法(C11+)
// 安全释放宏(防止 double free)
#define SAFE_FREE(p) do { if (p) { free(p); (p) = NULL; } } while(0)
以上六个知识点几乎涵盖了 C 语言“底层编程”面试的半壁江山。
想继续深入哪个部分?
A. 位运算经典算法题(位计数、只出现一次的数字等)
B. 共用体 + 位段 实现寄存器映射完整案例
C. 动态内存管理常见 bug 及 valgrind 检测方法
D. typedef + 函数指针 + 回调函数完整写法
E. 其他你想细化的点
告诉我字母,我们继续写代码和剖析!