1. 结构体与内存管理实战精要
1.1 结构体基础与内存布局
结构体是C语言中组织相关数据的核心工具。我们先看一个典型的学生结构体定义:
c复制struct student {
char name[20]; // 20字节字符数组
int age; // 4字节整型
};
这个结构体在32位系统中占用24字节(20+4),而在64位系统中由于内存对齐可能占用28字节。理解内存布局对优化程序性能至关重要。
三种初始化方式对比:
- 直接初始化:
struct student st = {"Tom", 20}; - 指定成员初始化:
struct student st = {.name="Tom", .age=20};(C99标准) - 逐成员赋值:
c复制struct student st; st.age = 20; strcpy(st.name, "Tom");
关键技巧:使用
strncpy替代strcpy可防止缓冲区溢出,如strncpy(st.name, "Tom", sizeof(st.name)-1);
1.2 堆栈内存分配实战
c复制struct student *p = malloc(sizeof(struct student));
if(p == NULL) {
// 错误处理
}
p->age = 20;
strncpy(p->name, "Tom", sizeof(p->name)-1);
free(p); // 必须释放
内存管理黄金法则:
- 每个
malloc必须对应一个free - 释放后立即将指针置NULL防止野指针
- 使用
calloc初始化内存为零
1.3 多级结构体嵌套
c复制struct Address {
char city[30];
char street[50];
};
struct Employee {
char name[20];
struct Address addr;
int salary;
};
访问方式:emp.addr.city。这种嵌套在数据库记录处理中非常常见。
2. 结构体高级应用与优化
2.1 结构体指针成员处理
当结构体包含指针成员时,内存管理需要特别注意:
c复制struct Person {
char *name; // 动态分配
int age;
};
// 正确使用方式
struct Person p;
p.name = malloc(100 * sizeof(char));
if(!p.name) { /* 处理错误 */ }
strcpy(p.name, "Dynamic Name");
free(p.name); // 必须先释放内部指针
2.2 结构体作为函数参数
值传递 vs 指针传递:
c复制// 低效方式(结构体拷贝)
void printStudent(struct student st);
// 高效方式(指针传递)
void printStudent(const struct student *st);
经验法则:超过16字节的结构体应使用指针传递。const修饰防止意外修改。
2.3 结构体数组动态分配
c复制struct student *createClass(int size) {
struct student *cls = calloc(size, sizeof(struct student));
if(!cls) return NULL;
return cls;
}
// 使用示例
struct student *myClass = createClass(30);
if(myClass) {
// 使用数组...
free(myClass);
}
3. 联合体与枚举深度解析
3.1 联合体内存共享机制
联合体所有成员共享同一内存空间,大小由最大成员决定:
c复制union Data {
int i;
float f;
char str[20];
}; // 占用20字节(由str决定)
典型应用场景:
- 协议解析(同一数据的不同表示)
- 类型转换
- 节省内存空间
3.2 枚举类型高级用法
c复制enum Color { RED=1, GREEN=3, BLUE=5 };
枚举本质是整型常量,但提供了更好的可读性。现代用法:
c复制typedef enum {
LOG_DEBUG,
LOG_INFO,
LOG_WARNING,
LOG_ERROR
} LogLevel;
枚举使用技巧:
- 显式指定值便于序列化
- 与switch语句配合使用
- 作为函数参数增强可读性
4. 文件操作核心实战
4.1 文件打开模式详解
| 模式 | 描述 | 文件存在 | 文件不存在 |
|---|---|---|---|
| "r" | 只读 | 正常打开 | 返回NULL |
| "w" | 只写 | 清空内容 | 创建新文件 |
| "a" | 追加 | 追加写入 | 创建新文件 |
| "r+" | 读写 | 正常打开 | 返回NULL |
| "w+" | 读写 | 清空内容 | 创建新文件 |
| "a+" | 读写 | 追加写入 | 创建新文件 |
二进制与文本模式区别:
- Windows系统中文本模式会转换
\n为\r\n - Linux/Unix系统无区别
- 二进制模式适合非文本数据
4.2 安全文件操作模板
c复制FILE *fp = fopen("data.txt", "r");
if(!fp) {
perror("文件打开失败");
return EXIT_FAILURE;
}
// 文件操作...
if(fclose(fp) == EOF) {
perror("文件关闭失败");
return EXIT_FAILURE;
}
4.3 高效文件读写技巧
逐行读取最佳实践:
c复制char buffer[1024];
while(fgets(buffer, sizeof(buffer), fp)) {
// 处理每行内容
buffer[strcspn(buffer, "\n")] = '\0'; // 移除换行符
printf("%s\n", buffer);
}
二进制数据读写:
c复制struct Record {
int id;
char name[50];
float value;
};
// 写入
struct Record rec = {1, "Test", 3.14};
fwrite(&rec, sizeof(struct Record), 1, fp);
// 读取
struct Record in_rec;
fread(&in_rec, sizeof(struct Record), 1, fp);
5. 文本加密与数据处理实战
5.1 简单加密算法实现
c复制void encrypt(char *str, int key) {
while(*str) {
*str += key; // 简单位移加密
str++;
}
}
void decrypt(char *str, int key) {
while(*str) {
*str -= key; // 解密
str++;
}
}
更安全的加密方案:
- 使用标准库函数如
memfrob - 实现XOR加密
- 集成加密库如OpenSSL
5.2 文件排序高级实现
动态内存分配版本:
c复制int *readNumbers(FILE *fp, int *count) {
int capacity = 10;
int *numbers = malloc(capacity * sizeof(int));
*count = 0;
char buffer[100];
while(fgets(buffer, sizeof(buffer), fp)) {
if(*count >= capacity) {
capacity *= 2;
numbers = realloc(numbers, capacity * sizeof(int));
}
numbers[(*count)++] = atoi(buffer);
}
return numbers;
}
排序算法选择:
- 小数据量:冒泡排序(简单)
- 中等数据:快速排序
- 大数据:外部排序
6. 高级文件处理技巧
6.1 文件位置控制
c复制fseek(fp, 0, SEEK_END); // 移动到文件末尾
long size = ftell(fp); // 获取文件大小
fseek(fp, 0, SEEK_SET); // 回到文件开头
随机访问应用:
- 数据库索引
- 日志文件分析
- 大数据处理
6.2 错误处理最佳实践
c复制errno = 0;
fread(buffer, sizeof(char), 100, fp);
if(ferror(fp)) {
perror("读取错误");
clearerr(fp);
}
6.3 性能优化技巧
- 使用缓冲区:
setvbuf设置自定义缓冲区 - 批量读写:减少IO操作次数
- 内存映射文件:处理超大文件
7. 综合案例:学生成绩管理系统
c复制struct Student {
int id;
char name[50];
float score;
};
void saveStudents(const char *filename, struct Student *students, int count) {
FILE *fp = fopen(filename, "wb");
if(!fp) return;
fwrite(&count, sizeof(int), 1, fp); // 先写入记录数
fwrite(students, sizeof(struct Student), count, fp);
fclose(fp);
}
struct Student *loadStudents(const char *filename, int *count) {
FILE *fp = fopen(filename, "rb");
if(!fp) return NULL;
fread(count, sizeof(int), 1, fp);
struct Student *students = malloc(*count * sizeof(struct Student));
fread(students, sizeof(struct Student), *count, fp);
fclose(fp);
return students;
}
这个案例展示了如何将结构体数组与文件操作结合,实现数据的持久化存储。