typedef struct Node { /* typedef 起名,后面直接用 Node */
int value;
struct Node *next; /* 自引用:只能是指针(大小未知前不能整体嵌入) */
} Node;
Node n = { 10, NULL }; /* 初始化列表按成员顺序 */
Node *p = &n;
n.value /* 直接访问:结构.成员 */
p->value /* 间接访问:指针->成员,等价于 (*p).value */两个不同结构类型的指针不能直接互赋(类型不同),尽管它们都是「地址」——编译器在做类型检查。不完整声明 struct B; 用于解决两个结构互相引用的死锁(A 里放 B*,B 里放 A*)。
结构成员按声明顺序排列,但中间可能塞「填充字节」让每个成员落在其「对齐边界」上——CPU 访问对齐数据更快,某些平台不对齐直接崩:
struct Bad { char c; double d; char e; }; /* 排布:c(1)+填7+d(8)+e(1)+填7 = 24 */
struct Good { double d; char c; char e; }; /* d(8)+c(1)+e(1)+填6 = 16 */两条实用法则:① 成员按大小降序排列能省空间;② 不要比较结构体本身(填充字节是随机值,== 不可用,memcmp 也不可靠),逐成员比较才对。
| 方式 | 开销 | 能否修改原结构 | 适用 |
|---|---|---|---|
| void f(struct S s) | 拷贝整个结构(大的很贵) | 否 | 小型只读结构,想要值语义隔离 |
| void f(struct S *ps) | 一个指针 | 能(const 修饰可禁写) | 大结构、需要回写、链表操作——默认选它 |
void grow(struct Rect *r, int dw) { r->w += dw; } /* 回写用指针 */
int area(const struct Rect *r) { return r->w * r->h; } /* 只读加 const */返回结构也是传值拷贝,小结构返回值很方便;大结构返回用「调用方传入输出指针」更高效。
位段(bit field):把成员指定到 bit 级,硬件寄存器/协议头打包利器:
struct Flags {
unsigned ready : 1; /* 1 bit */
unsigned mode : 3; /* 0~7 */
unsigned channel: 4; /* 0~15 */
}; /* 总共 1 字节出头(对齐后通常 1~2 字节) */联合(union):所有成员共享同一起始地址,大小 = 最大成员。同一块内存的多种解读:
union Reg {
unsigned int u32; /* 整体读 */
unsigned char b[4]; /* 按字节读:看字节序 */
};
union Reg r;
r.u32 = 0x12345678;
printf("%02X\n", r.b[0]); /* 78 = 小端;12 = 大端 —— 探测字节序的经典写法 */