结构体进阶:嵌套、传参、内存对齐与位段

结构体这东西,说白了就是C语言里用来“打包数据”的利器。基础用法大家都会,但真正到了项目里,怎么用好它、怎么避免踩坑,这里头门道不少。今天我就把结构体进阶的几个核心知识点掰开揉碎讲清楚。

一、结构体的嵌套

结构体里面再放结构体,这在实际项目中太常见了。比如描述一个学生,除了姓名学号,还有出生日期——日期本身又是一个结构体。

struct Date {
    int year;
    int month;
    int day;
};

struct Student {
    char name[32];
    int id;
    struct Date birthday;  // 嵌套结构体
};

访问的时候一层层点下去就行:stu.birthday.year。我见过有人写代码时忘了中间那层,直接写stu.year,编译器报错还一脸懵——嗯,这种错误我也犯过。

小技巧:嵌套结构体初始化可以用花括号嵌套,比如 struct Student stu = {"张三", 1001, {2000, 5, 15}};,这样代码可读性更好。

二、结构体作为函数参数

结构体传参有两种方式:传值 和 传指针。我建议你记住一句话:小结构传值,大结构传指针

传值会把整个结构体拷贝一份,如果结构体很大(比如几百字节),每次调用函数都要拷贝,性能就下来了。我在做嵌入式项目时,一个结构体里放了传感器校准参数,足足128字节,传值调用一次就要拷贝128字节,循环里调个几百次,CPU时间全浪费在拷贝上了。

// 传值——适合小结构体
void printStudent(struct Student s) {
    printf("Name: %s\n", s.name);
}

// 传指针——适合大结构体
void updateStudent(struct Student *ps) {
    ps->id = 1002;  // 用->访问成员
}

传指针还有个好处:可以在函数内部修改原结构体的内容。如果你不想被修改,加个const修饰就行:void show(const struct Student *ps)

注意:传指针时一定要检查指针是否为NULL。我曾经在一个驱动代码里忘了检查,结果传入空指针,程序直接跑飞。从那以后,我所有指针参数进来第一件事就是判空。

三、结构体的内存对齐

这个问题面试常考,项目里也经常遇到。说白了就是:编译器会在结构体成员之间插入填充字节,让每个成员的地址对齐到它自身大小的整数倍

为什么会这样?因为CPU访问对齐的数据更快。有些ARM芯片甚至不支持非对齐访问,一访问就异常。

struct Example1 {
    char a;    // 1字节
    int b;     // 4字节
    char c;    // 1字节
};

你猜这个结构体多大?不是1+4+1=6字节,而是12字节!

成员 偏移量 大小 填充
char a 0 1 0
int b 4 4 3字节(对齐到4)
char c 8 1 0
填充 9-11 3 结构体总大小对齐到最大成员

优化方法很简单:把大的成员往前放。比如改成 int b; char a; char c;,大小就变成8字节了。

核心原则:结构体总大小必须是最大成员大小的整数倍。成员偏移量必须是自身大小的整数倍。

四、位段

位段(bit-field)是C语言里一个比较“嵌入式”的特性。它允许你精确控制每个成员占多少位,非常适合用来描述硬件寄存器或者协议帧。

struct Flags {
    unsigned int ready : 1;   // 只占1位
    unsigned int error : 1;   // 只占1位
    unsigned int mode : 2;    // 占2位,可表示0-3
    unsigned int count : 4;   // 占4位,可表示0-15
};

这个结构体总共只占8位(1字节),而不是4个int的16字节。我在做CAN总线协议解析时,就用位段来解析报文ID和数据域,代码写起来非常直观。

避坑指南:位段有很多实现定义的行为。比如位段在内存中的排列顺序(大端/小端)、位段能否跨越字节边界,这些在不同编译器下可能不一样。我曾经把一个用位段写的协议解析代码从ARM平台移植到x86上,结果数据全乱了——就是因为位段布局不同。所以跨平台项目慎用位段,或者加上明确的平台判断。

另外,位段的成员类型必须是intunsigned intsigned int(C99之后也支持_Bool)。不能取位段成员的地址,因为位段可能不是从字节边界开始的。

知识体系总览

下面这张图把结构体进阶的四个核心知识点串起来了,方便你整体把握:

结构体进阶 结构体嵌套 一层层点下去访问 花括号嵌套初始化 函数参数 传值 vs 传指针 小结构传值,大结构传指针 内存对齐 填充字节,对齐到整数倍 大成员往前放可优化 位段 精确控制位数 跨平台慎用 掌握这些,结构体才算真正入门

结构体嵌套让数据组织更自然,传参方式影响性能,内存对齐影响空间和访问效率,位段则给了你比特级的控制力。这四个知识点在实际项目中环环相扣,你想想看,一个网络协议栈里,报文结构体既要嵌套多层头部,又要考虑对齐,还可能用到位段解析标志位——全用上了。

好了,这一章就到这里。记住我一句话:结构体用得好,代码质量差不了


公众号:蓝海资料掘金营,微信deep3321