1. 结构体与共用体的本质区别
结构体(struct)和共用体(union)是C语言中两种完全不同的复合数据类型。结构体的每个成员拥有独立的内存空间,而共用体的所有成员共享同一块内存区域。这个根本差异决定了它们完全不同的应用场景。
关键区别:结构体适合描述具有多个属性的实体(如学生信息包含学号、姓名、成绩等),而共用体适合需要节省内存或同一时刻只需使用一个成员的场景(如网络协议解析)。
1.1 内存布局对比
结构体的内存占用是所有成员大小的总和(考虑对齐):
c复制struct Student {
int id; // 4字节
char name[20]; // 20字节
float score; // 4字节
}; // 总大小通常为28字节(取决于对齐方式)
共用体的内存占用是其最大成员的大小:
c复制union Data {
int i; // 4字节
float f; // 4字节
char str[20]; // 20字节
}; // 总大小为20字节
1.2 典型应用场景
结构体常见用途:
- 数据库记录存储
- 图形编程中的点坐标
- 文件系统元数据描述
共用体典型应用:
- 协议报文解析(同一内存区域可能存储不同类型数据)
- 硬件寄存器访问(同一地址可能对应不同功能)
- 类型转换技巧(通过共用体实现无转换的类型解释)
2. 结构体深度解析
2.1 声明与初始化
完全声明方式:
c复制struct Point {
int x;
int y;
};
struct Point p1 = {10, 20}; // 传统初始化
struct Point p2 = {.y=30, .x=40}; // C99指定初始化器
匿名结构体(C11特性):
c复制struct {
char model[20];
float price;
} car1, car2; // 直接定义变量
2.2 结构体嵌套
多层嵌套示例:
c复制struct Date {
int year;
int month;
int day;
};
struct Employee {
int id;
struct Date hire_date; // 嵌套结构体
struct {
char city[30];
char street[50];
} address; // 匿名嵌套
};
2.3 位域技术
精确控制结构体成员位数:
c复制struct PackedData {
unsigned int flag1 : 1; // 1位
unsigned int flag2 : 3; // 3位
unsigned int type : 4; // 4位
unsigned int value : 24; // 24位
}; // 总共32位(4字节)
注意事项:位域成员不能取地址(&操作),其具体存储顺序取决于编译器实现。
3. 共用体高级用法
3.1 类型转换技巧
通过共用体实现无转换的类型解释:
c复制union Converter {
float f;
unsigned int u;
} conv;
conv.f = 3.14;
printf("Float %f as unsigned: 0x%X\n", conv.f, conv.u);
3.2 协议解析应用
网络协议头解析示例:
c复制union IPHeader {
struct {
unsigned int version : 4;
unsigned int ihl : 4;
unsigned char tos;
unsigned short tot_len;
// 其他字段...
} fields;
unsigned char raw[20];
} header;
3.3 硬件寄存器访问
嵌入式开发中的寄存器映射:
c复制union GPIOReg {
struct {
uint32_t data : 16;
uint32_t mode : 8;
uint32_t status : 8;
} bits;
uint32_t word;
} *reg = (union GPIOReg*)0x40020000;
4. 结构体与共用体的结合使用
4.1 变体记录实现
数据库记录存储方案:
c复制struct VariantRecord {
int type;
union {
int i_val;
float f_val;
char str_val[50];
} data;
};
void printRecord(struct VariantRecord r) {
switch(r.type) {
case 0: printf("Integer: %d\n", r.data.i_val); break;
case 1: printf("Float: %f\n", r.data.f_val); break;
case 2: printf("String: %s\n", r.data.str_val); break;
}
}
4.2 内存高效存储
图形程序中的颜色表示:
c复制struct Color {
enum { RGB, RGBA, CMYK } format;
union {
struct { unsigned char r, g, b; } rgb;
struct { unsigned char r, g, b, a; } rgba;
struct { unsigned char c, m, y, k; } cmyk;
} value;
};
5. 高级话题与性能优化
5.1 结构体对齐控制
显式对齐设置(GCC/Clang):
c复制struct __attribute__((packed)) TightPacked {
char a;
int b;
char c;
}; // 大小为6字节(1+4+1)
struct __attribute__((aligned(16))) AlignedStruct {
double data;
}; // 16字节对齐
5.2 柔性数组成员
动态大小结构体:
c复制struct DynArray {
int length;
float data[]; // 柔性数组成员
};
struct DynArray *createArray(int n) {
struct DynArray *arr = malloc(sizeof(struct DynArray) + n*sizeof(float));
arr->length = n;
return arr;
}
5.3 结构体函数指针
面向对象风格编程:
c复制struct Calculator {
int (*add)(int, int);
int (*sub)(int, int);
};
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
struct Calculator calc = {add, sub};
printf("5+3=%d\n", calc.add(5,3));
6. 常见问题与调试技巧
6.1 内存对齐问题排查
调试不对齐访问(x86平台示例):
c复制struct Misaligned {
char c;
int i; // 可能不对齐
};
// 使用offsetof宏检查偏移量
printf("i offset: %zu\n", offsetof(struct Misaligned, i));
6.2 共用体数据覆盖
检测非法访问:
c复制union SafeUnion {
int type;
struct {
int type; // 冗余字段用于验证
float data;
} f;
struct {
int type;
char str[20];
} s;
};
void processUnion(union SafeUnion u) {
if(u.type != u.f.type || u.type != u.s.type) {
fprintf(stderr, "Union corrupted!\n");
}
}
6.3 结构体初始化陷阱
部分初始化风险:
c复制struct Config {
int timeout;
char path[256];
int flags;
};
struct Config cfg = {10}; // 只有timeout被初始化,其余为随机值
// 更安全的做法:
struct Config cfg_safe = {
.timeout = 10,
.path = "",
.flags = 0
};
7. 现代C语言新特性
7.1 复合字面量(C99)
直接创建临时结构体:
c复制// 传统方式
struct Point p;
p.x = 10; p.y = 20;
// 复合字面量
drawLine((struct Point){10,20}, (struct Point){30,40});
7.2 匿名结构与联合(C11)
简化嵌套结构:
c复制struct Device {
int id;
union { // 匿名联合
struct { int x, y; };
struct { float lat, lon; };
};
};
struct Device dev;
dev.x = 10; // 直接访问
dev.lat = 3.14; // 会覆盖x/y
7.3 类型泛型(C11)
共用体实现泛型函数:
c复制#define print_value(x) _Generic((x), \
int: print_int, \
float: print_float, \
char*: print_string)(x)
void print_int(int i) { printf("%d\n", i); }
void print_float(float f) { printf("%f\n", f); }
void print_string(char *s) { printf("%s\n", s); }
