一、结构体(struct)
1. 基本定义与初始化
#include <stdio.h>
// 定义结构体
struct Point {
int x;
int y;
};
int main() {
// 初始化方式1:声明时初始化
struct Point p1 = {10, 20};
// 初始化方式2:逐个赋值
struct Point p2;
p2.x = 30;
p2.y = 40;
printf("p1: (%d, %d)\n", p1.x, p1.y);
printf("p2: (%d, %d)\n", p2.x, p2.y);
return 0;
}
2. 结构体嵌套
struct Address {
char city[50];
int zip;
};
struct Person {
char name[50];
int age;
struct Address addr; // 嵌套结构体
};
int main() {
struct Person p = {"Alice", 25, {"Beijing", 100000}};
printf("%s lives in %s, zip: %d\n", p.name, p.addr.city, p.addr.zip);
return 0;
}
3. 结构体指针与 -> 操作符
void printPoint(struct Point *p) {
printf("Point: (%d, %d)\n", p->x, p->y);
}
int main() {
struct Point p = {5, 6};
printPoint(&p); // 传地址
return 0;
}
4. typedef 简化结构体声明
typedef struct {
double real;
double imag;
} Complex;
int main() {
Complex c1 = {3.0, 4.0};
printf("Complex number: %.1f + %.1fi\n", c1.real, c1.imag);
return 0;
}
5. 结构体数组
#define N 3
struct Student {
char name[20];
int score;
};
int main() {
struct Student students[N] = {
{"Tom", 88},
{"Jerry", 92},
{"Spike", 75}
};
for (int i = 0; i < N; i++) {
printf("%s: %d\n", students[i].name, students[i].score);
}
return 0;
}
6. 结构体作为函数参数(值传递 vs 指针传递)
- 值传递:复制整个结构体(开销大)
- 指针传递:高效,推荐
// 值传递(不推荐大数据结构)
void updateByValue(struct Point p) {
p.x += 10; // 修改的是副本
}
// 指针传递(推荐)
void updateByPointer(struct Point *p) {
p->x += 10;
}
7. 结构体对齐与 #pragma pack
默认情况下,编译器会对结构体成员进行内存对齐以提升访问效率。可通过 #pragma pack 控制:
#pragma pack(1) // 取消对齐,按1字节对齐
struct PackedStruct {
char a; // 1 byte
int b; // 4 bytes
short c; // 2 bytes
}; // 总大小:7 bytes(而非默认的12)
#pragma pack() // 恢复默认对齐
使用场景:网络协议、文件格式解析等需要精确控制内存布局的情况。
二、联合体(union)
联合体的所有成员共享同一块内存,大小等于最大成员的大小。
1. 基本用法
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
data.i = 10;
printf("data.i = %d\n", data.i);
data.f = 220.5;
printf("data.f = %.1f\n", data.f);
// 注意:此时 data.i 的值已不可靠!
strcpy(data.str, "Hello");
printf("data.str = %s\n", data.str);
return 0;
}
⚠️ 联合体一次只能安全使用一个成员!
2. 联合体 + 枚举实现“类型安全”的变体(类似 tagged union)
enum TypeTag { INT, FLOAT, STRING };
struct Variant {
enum TypeTag type;
union {
int i;
float f;
char* s;
} data;
};
void printVariant(struct Variant v) {
switch (v.type) {
case INT: printf("Int: %d\n", v.data.i); break;
case FLOAT: printf("Float: %.2f\n", v.data.f); break;
case STRING: printf("String: %s\n", v.data.s); break;
}
}
int main() {
struct Variant v1 = {INT, .data.i = 42};
struct Variant v2 = {STRING, .data.s = "Union example"};
printVariant(v1);
printVariant(v2);
return 0;
}
这是 C 语言中模拟“多态”或“可变类型”的常用技巧。
3. 联合体用于字节级操作(如大小端检测)
int isLittleEndian() {
union {
uint32_t i;
uint8_t c[4];
} u = {0x12345678};
return u.c[0] == 0x78; // 小端:低位字节在低地址
}
int main() {
if (isLittleEndian()) {
printf("This machine is little-endian.\n");
} else {
printf("This machine is big-endian.\n");
}
return 0;
}
4. 联合体 vs 结构体内存对比
struct S { int a; char b; double c; }; // sizeof ≈ 16(对齐后)
union U { int a; char b; double c; }; // sizeof = 8(double 最大)
printf("sizeof(struct S) = %zu\n", sizeof(struct S));
printf("sizeof(union U) = %zu\n", sizeof(union U));
三、常见应用场景总结
| 场景 | 推荐类型 |
|---|---|
| 表示一个对象的多个属性(如学生信息) | struct |
| 节省内存,多个变量不会同时使用 | union |
| 网络/硬件协议解析(固定内存布局) | struct + #pragma pack |
| 实现动态类型(带标签) | union + enum |
| 位域操作(节省空间) | struct 中的位域(bit-field) |
补充:位域示例
struct Flags {
unsigned int is_ready : 1; // 1 bit
unsigned int is_valid : 1; // 1 bit
unsigned int count : 6; // 6 bits (共1字节)
};
四、注意事项
- 结构体:成员独立存储,总大小 ≥ 各成员之和(考虑对齐)。
- 联合体:所有成员共享内存,总大小 = 最大成员大小。
- 联合体不能直接整体赋值(C99 起支持初始化和赋值,但需谨慎)。
- 使用联合体时务必清楚当前有效成员,避免未定义行为。
如果你有特定应用场景(如嵌入式、协议解析、图形编程等),我可以提供更针对性的示例!