楼层: 首页/ 软件技术/ C 语言/ 结构体、共用体、枚举、typedef
6

结构体、共用体、枚举、typedef

struct · union · enum · typedef

数组只能装"同一类东西"。可现实里一个学生有姓名(字符串)、年龄(int)、成绩(double)——struct 就是把不同类型的数据打包成一个"自定义类型"。这是 C 迈向"组织复杂数据"的第一步。

struct 结构体

定义、初始化、访问成员

#include <stdio.h> #include <string.h> // 定义一个"学生"类型 struct Student { char name[20]; int age; double score; }; int main() { // 初始化 struct Student s = {"小明", 18, 92.5}; printf("%s %d %.1f\n", s.name, s.age, s.score); // 用 . 访问成员 s.age = 19; // 修改 // 结构体指针用 -> 访问成员,等价于 (*p).name struct Student *p = &s; printf("名字:%s\n", p->name); return 0; }

运行结果

小明 18 92.5 名字:小明

记. 和 -> 的区别

结构体变量用 .:s.name。
结构体指针用 ->:p->name。
其实 p->name 就是 (*p).name 的简写,C 嫌它啰嗦才发明了箭头。记法:手里是实体就点,手里是地址就箭头。

union 共用体:所有人挤同一间房

union 和 struct 长得像,但行为相反:所有成员共享同一块内存,同一时刻只能用一个。它的大小等于最大的那个成员。常见于"这个字段可能是 int 也可能是 float"这类省内存场景。

union 的共享内存特性

union Data { int i; float f; char str[16]; }; // 大小取最大成员 str 的 16 字节 union Data d; d.i = 65; printf("%d\n", d.i); // 65 d.f = 3.14f; // 现在 f 和 i 是同一块内存,再读 d.i 就变成无意义的乱数了

enum 枚举与 typedef 别名

枚举给数字起名字;typedef 给类型起别名

enum Color { RED, GREEN, BLUE }; // 实际是 0,1,2,但用名字比数字可读 enum Color c = GREEN; // typedef 给长类型起个短名字 typedef unsigned long u64; // 以后 u64 就是 unsigned long // 结构体 + typedef 的经典组合,省得每次写 struct typedef struct { int x, y; } Point; Point p = {3, 4}; // 不用再写 struct Point

结构体内存对齐与位域

你以为结构体大小是各成员相加?天真。编译器为了 CPU 读取更快,会在成员之间"垫"一些空字节——这就是内存对齐。所以 sizeof(struct) 往往比你手算的大。

对齐实测:把大成员放前面能省空间

typedef struct { char c; // 1 字节 int i; // 4 字节,但要对齐到 4 的倍数:c 后面会被垫 3 字节 char d; // 1 字节 } Bad; // sizeof = 12 而不是 6! typedef struct { int i; // 4 字节先放 char c; // 1 字节 char d; // 1 字节,和 c 挤一起 } Good; // sizeof = 8 // 口诀:按成员大小从大到小排,省垫补。offsetof(struct, member) 可量出偏移。

强制对齐:跨平台通信、硬件寄存器映射时需要紧凑布局,用 #pragma pack(1) 告诉编译器"别垫字节,按 1 字节对齐":

#pragma pack(push, 1) typedef struct { char cmd; int len; } Packet; // sizeof 严格按 1+4=5,不垫补 #pragma pack(pop) // 恢复之前的对齐设置

位域(bit-field):连字节都嫌多,想直接用"几个 bit"。适合标志位、协议头:

struct Flags { unsigned int ready : 1; // 只占 1 个 bit unsigned int mode : 3; // 占 3 个 bit(0~7) unsigned int err : 4; // 占 4 个 bit }; // 总共 8 bit = 1 字节 struct Flags f; f.ready = 1; // 直接当成员用,编译器帮你位运算