c语言 typedef、共用体、位运算、位段、枚举类型、内存管理

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

典型用途

  1. 节省内存(嵌入式最常见)
  2. 类型转换 / 内存解释(查看内存的多种解释)
  3. 实现类似 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);
}

内存管理高频陷阱

  1. 释放后使用(use-after-free)
  2. 重复释放(double free)
  3. 释放非动态分配的内存(free 栈上变量)
  4. 忘记释放(内存泄漏)
  5. 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. 其他你想细化的点

告诉我字母,我们继续写代码和剖析!

文章已创建 4138

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部