目录
[1.1 结构体的基本概念](#1.1 结构体的基本概念)
[1.2 结构体定义格式](#1.2 结构体定义格式)
[1.3 结构体变量的使用](#1.3 结构体变量的使用)
[1.4 typedef 起别名](#1.4 typedef 起别名)
[1.5 结构体定义位置](#1.5 结构体定义位置)
[2.1 结构体数组定义与初始化](#2.1 结构体数组定义与初始化)
[2.2 结构体数组练习:投票系统](#2.2 结构体数组练习:投票系统)
[3.1 值传递 vs 地址传递](#3.1 值传递 vs 地址传递)
[3.2 结构体参数传递对比](#3.2 结构体参数传递对比)
[3.3 函数返回结构体](#3.3 函数返回结构体)
[4.1 嵌套结构体定义](#4.1 嵌套结构体定义)
[4.2 多层嵌套](#4.2 多层嵌套)
[5.1 结构体指针访问](#5.1 结构体指针访问)
[5.2 箭头运算符 vs 点运算符](#5.2 箭头运算符 vs 点运算符)
[6.1 内存对齐规则](#6.1 内存对齐规则)
[6.2 内存对齐示例](#6.2 内存对齐示例)
[6.3 内存布局可视化](#6.3 内存布局可视化)
[6.4 优化建议](#6.4 优化建议)
[6.5 强制对齐(pragma)](#6.5 强制对齐(pragma))
[7.1 共用体定义与使用](#7.1 共用体定义与使用)
[7.2 共用体内存特点](#7.2 共用体内存特点)
[7.3 共用体应用场景](#7.3 共用体应用场景)
[9.1 学生管理系统](#9.1 学生管理系统)
[9.2 配置文件解析器](#9.2 配置文件解析器)
结构体 :将多个不同类型的数据组合成一个整体的自定义数据类型
共用体 :多个变量共享同一块内存空间的特殊数据类型
核心价值:组织复杂数据、提高代码可读性、实现数据抽象
1、结构体定义与使用
1.1 结构体的基本概念
结构体 = 一批相关数据的集合
每个数据称为"成员"或"字段"
适用于描述具有多个属性的事物
应用场景举例:
- 学生信息(姓名、年龄、成绩)
- 员工信息(工号、部门、工资)
- 商品信息(编号、名称、价格、库存)
- 坐标点(x, y, z)
1.2 结构体定义格式
#include <stdio.h>
#include <string.h>
// 方式1:标准定义
struct Student {
char name[100];
int age;
float score;
};
// 方式2:定义同时创建变量
struct Teacher {
char name[50];
int age;
} teacher1, teacher2;
// 方式3:匿名结构体(只能使用一次)
struct {
int x;
int y;
} point = {10, 20};
int main() {
// 使用结构体定义变量
struct Student stu1;
struct Student stu2;
// 方式4:定义时初始化
struct Student stu3 = {"张三", 20, 95.5};
return 0;
}
1.3 结构体变量的使用
#include <stdio.h>
#include <string.h>
struct GirlFriend {
char name[100];
int age;
char gender;
double height;
};
int main() {
// 定义结构体变量
struct GirlFriend gf1;
// 成员赋值(使用.运算符)
strcpy(gf1.name, "小诗诗");
gf1.age = 23;
gf1.gender = 'F';
gf1.height = 1.63;
// 输出打印
printf("=== 女朋友信息 ===\n");
printf("姓名: %s\n", gf1.name);
printf("年龄: %d\n", gf1.age);
printf("性别: %c\n", gf1.gender);
printf("身高: %.2f米\n", gf1.height);
// 结构体整体赋值
struct GirlFriend gf2 = gf1; // 拷贝所有成员
printf("\n拷贝后 gf2 姓名: %s\n", gf2.name);
return 0;
}
1.4 typedef 起别名
#include <stdio.h>
#include <string.h>
// 方式1:给结构体起别名
typedef struct {
char name[100];
int attack;
int defense;
int blood;
} Ultraman;
// 方式2:同时保留原名和别名
typedef struct Student {
char name[50];
int id;
float score;
} Stu;
int main() {
// 使用别名定义变量(更简洁)
Ultraman taro = {"泰罗", 100, 90, 500};
Ultraman leo = {"雷欧", 90, 80, 450};
Ultraman edo = {"艾迪", 120, 70, 600};
// 放入数组
Ultraman arr[] = {taro, leo, edo};
int len = sizeof(arr) / sizeof(arr[0]);
printf("=== 奥特曼信息 ===\n");
for (int i = 0; i < len; i++) {
printf("%-10s 攻击:%3d 防御:%3d 血量:%4d\n",
arr[i].name, arr[i].attack, arr[i].defense, arr[i].blood);
}
// 使用Stu别名
Stu s1 = {"张三", 1001, 88.5};
printf("\n学生: %s, 学号: %d, 成绩: %.1f\n", s1.name, s1.id, s1.score);
return 0;
}
1.5 结构体定义位置
| 位置 | 作用域 | 生命周期 | 推荐使用 |
|---|---|---|---|
| 函数外(全局) | 所有函数可用 | 程序全程 | ✅ 推荐 |
| 函数内(局部) | 仅本函数可用 | 函数执行期间 | ❌ 不推荐 |
#include <stdio.h>
// ✅ 全局定义(推荐)
struct GlobalStruct {
int value;
};
void func1() {
struct GlobalStruct gs; // 可以使用
}
void func2() {
// ❌ 局部定义(不推荐)
struct LocalStruct {
int value;
};
struct LocalStruct ls; // 仅func2可用
}
int main() {
struct GlobalStruct gs; // ✅ 可以使用
// struct LocalStruct ls; // ❌ 无法使用
return 0;
}
2、结构体数组
2.1 结构体数组定义与初始化
#include <stdio.h>
#include <string.h>
struct Student {
char name[50];
int age;
float score;
};
int main() {
// 方式1:先定义后赋值
struct Student stu1 = {"张三", 20, 85.5};
struct Student stu2 = {"李四", 21, 90.0};
struct Student stu3 = {"王五", 22, 78.5};
// 方式2:直接初始化数组
struct Student stuArr[3] = {
{"张三", 20, 85.5},
{"李四", 21, 90.0},
{"王五", 22, 78.5}
};
// 方式3:部分初始化
struct Student stuArr2[5] = {
{"张三", 20, 85.5},
{"李四", 21, 90.0}
// 剩余元素自动初始化为0/"\0"
};
// 遍历数组
printf("=== 学生信息表 ===\n");
printf("%-15s %-5s %-8s\n", "姓名", "年龄", "成绩");
printf("----------------------------\n");
for (int i = 0; i < 3; i++) {
printf("%-15s %-5d %-8.1f\n",
stuArr[i].name, stuArr[i].age, stuArr[i].score);
}
return 0;
}
2.2 结构体数组练习:投票系统
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
struct Spot {
char name[20];
int count;
};
int main() {
// 1. 定义4个景点
struct Spot arr[4] = {
{"故宫", 0},
{"长城", 0},
{"天坛", 0},
{"颐和园", 0}
};
int spotCount = 4;
int voterCount = 80;
// 2. 设置随机数种子
srand(time(NULL));
// 3. 模拟80名同学投票
printf("=== 模拟投票 ===\n");
for (int i = 0; i < voterCount; i++) {
int choose = rand() % spotCount; // 0-3
arr[choose].count++;
}
// 4. 显示投票结果
printf("\n=== 投票结果 ===\n");
printf("%-15s %-10s\n", "景点", "票数");
printf("----------------------------\n");
for (int i = 0; i < spotCount; i++) {
printf("%-15s %-10d\n", arr[i].name, arr[i].count);
}
// 5. 找出最高票
int maxCount = arr[0].count;
for (int i = 1; i < spotCount; i++) {
if (arr[i].count > maxCount) {
maxCount = arr[i].count;
}
}
// 6. 显示获胜者(可能有多个)
printf("\n=== 最受欢迎景点 ===\n");
for (int i = 0; i < spotCount; i++) {
if (arr[i].count == maxCount) {
printf("🏆 %s (%d票)\n", arr[i].name, arr[i].count);
}
}
return 0;
}
3、结构体作为函数参数
3.1 值传递 vs 地址传递
#include <stdio.h>
#include <string.h>
typedef struct {
char name[50];
int age;
} Student;
// 方式1:值传递(拷贝整个结构体)
void modifyByValue(Student stu) {
strcpy(stu.name, "李四");
stu.age = 25;
printf("函数内: %s, %d\n", stu.name, stu.age);
}
// 方式2:地址传递(推荐,效率高)
void modifyByPointer(Student *stu) {
strcpy(stu->name, "王五");
stu->age = 30;
printf("函数内: %s, %d\n", stu->name, stu->age);
}
// 方式3:const指针(只读,不修改)
void printStudent(const Student *stu) {
printf("只读访问: %s, %d\n", stu->name, stu->age);
// stu->age = 100; // ❌ 编译错误
}
int main() {
Student s1 = {"张三", 20};
printf("原始数据: %s, %d\n\n", s1.name, s1.age);
// 值传递:不影响原数据
printf("【值传递】\n");
modifyByValue(s1);
printf("调用后: %s, %d\n\n", s1.name, s1.age); // 不变
// 地址传递:影响原数据
printf("【地址传递】\n");
modifyByPointer(&s1);
printf("调用后: %s, %d\n\n", s1.name, s1.age); // 已变
// const指针:只读访问
printf("【const指针】\n");
printStudent(&s1);
return 0;
}
3.2 结构体参数传递对比
| 传递方式 | 语法 | 效率 | 是否修改原值 | 适用场景 |
|---|---|---|---|---|
| 值传递 | func(Student s) |
低(拷贝) | ❌ 否 | 小结构体、只读 |
| 指针传递 | func(Student *s) |
高(地址) | ✅ 是 | 大结构体、需修改 |
| const指针 | func(const Student *s) |
高(地址) | ❌ 否 | 大结构体、只读 |
3.3 函数返回结构体
#include <stdio.h>
#include <string.h>
typedef struct {
int id;
char name[50];
float score;
} Student;
// 返回结构体(C99支持)
Student createStudent(int id, const char *name, float score) {
Student s;
s.id = id;
strcpy(s.name, name);
s.score = score;
return s; // 返回整个结构体
}
// 返回结构体指针(注意生命周期)
Student* createStudentPtr(int id, const char *name, float score) {
// ❌ 错误:返回局部变量地址
// Student s;
// return &s;
// ✅ 正确:动态分配
Student *s = (Student*)malloc(sizeof(Student));
s->id = id;
strcpy(s->name, name);
s->score = score;
return s;
}
// 返回多个值(通过指针参数)
void getTopStudent(Student students[], int len, Student *top) {
*top = students[0];
for (int i = 1; i < len; i++) {
if (students[i].score > top->score) {
*top = students[i];
}
}
}
int main() {
// 返回结构体
Student s1 = createStudent(1001, "张三", 95.5);
printf("学生: %s, 成绩: %.1f\n", s1.name, s1.score);
// 返回结构体指针
Student *s2 = createStudentPtr(1002, "李四", 88.0);
printf("学生: %s, 成绩: %.1f\n", s2->name, s2->score);
free(s2); // 记得释放
// 返回多个值
Student arr[] = {
{1, "A", 85.0},
{2, "B", 92.0},
{3, "C", 78.0}
};
Student top;
getTopStudent(arr, 3, &top);
printf("最高分: %s (%.1f)\n", top.name, top.score);
return 0;
}
4、结构体嵌套
4.1 嵌套结构体定义
#include <stdio.h>
#include <string.h>
// 子结构体:联系方式
struct Contact {
char phone[20];
char email[50];
char address[100];
};
// 主结构体:学生信息
struct Student {
char name[50];
int age;
char gender;
float score;
struct Contact contact; // 嵌套结构体
};
int main() {
// 方式1:逐个赋值
struct Student stu1;
strcpy(stu1.name, "张三");
stu1.age = 20;
stu1.gender = 'M';
stu1.score = 88.5;
strcpy(stu1.contact.phone, "13812345678");
strcpy(stu1.contact.email, "zhangsan@qq.com");
strcpy(stu1.contact.address, "北京市朝阳区");
// 方式2:初始化列表
struct Student stu2 = {
"李四",
21,
'F',
92.0,
{"13987654321", "lisi@qq.com", "上海市浦东新区"}
};
// 访问嵌套成员
printf("=== 学生信息 ===\n");
printf("姓名: %s\n", stu2.name);
printf("年龄: %d\n", stu2.age);
printf("电话: %s\n", stu2.contact.phone);
printf("邮箱: %s\n", stu2.contact.email);
printf("地址: %s\n", stu2.contact.address);
return 0;
}
4.2 多层嵌套
#include <stdio.h>
#include <string.h>
// 最内层:日期
struct Date {
int year;
int month;
int day;
};
// 中间层:地址
struct Address {
char province[50];
char city[50];
char street[100];
};
// 最外层:员工
struct Employee {
int id;
char name[50];
float salary;
struct Date hireDate;
struct Address addr;
};
int main() {
struct Employee emp = {
1001,
"王五",
15000.0,
{2020, 6, 15},
{"广东省", "深圳市", "南山区科技园"}
};
printf("=== 员工信息 ===\n");
printf("工号: %d\n", emp.id);
printf("姓名: %s\n", emp.name);
printf("薪资: %.2f\n", emp.salary);
printf("入职日期: %d-%02d-%02d\n",
emp.hireDate.year, emp.hireDate.month, emp.hireDate.day);
printf("地址: %s%s%s\n",
emp.addr.province, emp.addr.city, emp.addr.street);
return 0;
}
5、结构体与指针
5.1 结构体指针访问
#include <stdio.h>
#include <string.h>
typedef struct {
int id;
char name[50];
float score;
} Student;
int main() {
Student stu = {1001, "张三", 88.5};
// 结构体指针
Student *pStu = &stu;
// 访问方式对比
printf("=== 访问方式对比 ===\n");
printf("stu.id = %d\n", stu.id); // 直接访问
printf("(*pStu).id = %d\n", (*pStu).id); // 解引用后访问
printf("pStu->id = %d\n", pStu->id); // 箭头运算符(推荐)
printf("\nstu.name = %s\n", stu.name);
printf("pStu->name = %s\n", pStu->id);
// 修改成员
pStu->score = 95.0;
printf("\n修改后成绩: %.1f\n", stu.score);
// 指针数组
Student stu2 = {1002, "李四", 90.0};
Student stu3 = {1003, "王五", 85.0};
Student *arr[] = {&stu, &stu2, &stu3};
int len = sizeof(arr) / sizeof(arr[0]);
printf("\n=== 指针数组遍历 ===\n");
for (int i = 0; i < len; i++) {
printf("%s: %.1f分\n", arr[i]->name, arr[i]->score);
}
return 0;
}
5.2 箭头运算符 vs 点运算符
| 运算符 | 使用场景 | 示例 |
|---|---|---|
. |
结构体变量 | stu.name |
-> |
结构体指针 | pStu->name |
// 等价关系
(*pStu).name == pStu->name
6、结构体内存对齐
6.1 内存对齐规则
规则1:首地址规则
结构体首地址能被最大对齐系数整除
规则2:成员对齐规则
每个成员地址必须是其自身大小(或对齐系数)的整数倍
规则3:总大小规则
结构体总大小必须是最大成员大小的整数倍(向上取整)
6.2 内存对齐示例
#include <stdio.h>
// 示例1:未优化(24字节)
struct Layout1 {
double a; // 8字节,偏移0-7
char b; // 1字节,偏移8
// 填充3字节,偏移9-11
int c; // 4字节,偏移12-15
char d; // 1字节,偏移16
// 填充7字节,偏移17-23
}; // 总大小:24字节
// 示例2:优化后(16字节)
struct Layout2 {
char b; // 1字节,偏移0
char d; // 1字节,偏移1
// 填充2字节,偏移2-3
int c; // 4字节,偏移4-7
double a; // 8字节,偏移8-15
}; // 总大小:16字节
// 示例3:最优排列(13字节→16字节对齐)
struct Layout3 {
char b; // 1字节
char d; // 1字节
int c; // 4字节
double a; // 8字节
}; // 总大小:16字节(8的倍数)
int main() {
printf("Layout1大小: %zu 字节\n", sizeof(struct Layout1)); // 24
printf("Layout2大小: %zu 字节\n", sizeof(struct Layout2)); // 16
printf("Layout3大小: %zu 字节\n", sizeof(struct Layout3)); // 16
printf("\n节省空间: %zu 字节 (%.1f%%)\n",
sizeof(struct Layout1) - sizeof(struct Layout2),
(sizeof(struct Layout1) - sizeof(struct Layout2)) * 100.0 / sizeof(struct Layout1));
return 0;
}
6.3 内存布局可视化
struct Layout1 (24字节):
┌────────┬────────┬────────┬────────┐
│ double a (8) │ 偏移 0-7 │
├────────┼────────┼────────┼────────┤
│ char b │ 填充 │ 填充 │ 填充 │ 偏移 8-11
├────────┴────────┴────────┴────────┤
│ int c (4) │ 偏移 12-15 │
├────────┬────────┬────────┬────────┤
│ char d │ 填充 (7字节) │ 偏移 16-23
└────────┴────────┴────────┴────────┘
struct Layout2 (16字节):
┌────────┬────────┬────────┬────────┐
│ char b │ char d │ 填充 │ 填充 │ 偏移 0-3
├────────┴────────┴────────┴────────┤
│ int c (4) │ 偏移 4-7 │
├────────┬────────┬────────┬────────┤
│ double a (8) │ 偏移 8-15 │
└────────┴────────┴────────┴────────┘
节省:8字节 (33.3%)
6.4 优化建议
// ✅ 推荐:按大小排序(小到大或大到小)
struct Optimized {
char a; // 1
char b; // 1
short c; // 2
int d; // 4
double e; // 8
}; // 16字节
// ❌ 不推荐:大小交错
struct Unoptimized {
double e; // 8
char a; // 1
int d; // 4
char b; // 1
short c; // 2
}; // 24字节
6.5 强制对齐(pragma)
#include <stdio.h>
// 默认对齐
struct Default {
char a;
int b;
char c;
};
// 1字节对齐(紧凑,但可能降低性能)
#pragma pack(push, 1)
struct Packed {
char a;
int b;
char c;
};
#pragma pack(pop)
int main() {
printf("默认对齐: %zu 字节\n", sizeof(struct Default)); // 12
printf("1字节对齐: %zu 字节\n", sizeof(struct Packed)); // 6
return 0;
}
⚠️ 注意:强制对齐可能降低CPU访问效率,仅在内存紧张时使用
7、共用体(联合体)
7.1 共用体定义与使用
#include <stdio.h>
#include <string.h>
// 定义共用体
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
// 每次只能使用一个成员
data.i = 10;
printf("整数: %d\n", data.i);
data.f = 20.5;
printf("浮点: %.2f\n", data.f);
// printf("%d\n", data.i); // ❌ 值已改变
strcpy(data.str, "Hello");
printf("字符串: %s\n", data.str);
return 0;
}
7.2 共用体内存特点
#include <stdio.h>
union Money {
int moneyi; // 4字节
double moneyd; // 8字节
char moneystr[100]; // 100字节
};
int main() {
union Money money;
// 所有成员共享同一地址
printf("moneyi地址: %p\n", (void*)&money.moneyi);
printf("moneyd地址: %p\n", (void*)&money.moneyd);
printf("moneystr地址: %p\n", (void*)&money.moneystr);
// 大小 = 最大成员(考虑对齐)
printf("moneyi大小: %zu\n", sizeof(money.moneyi)); // 4
printf("moneyd大小: %zu\n", sizeof(money.moneyd)); // 8
printf("moneystr大小: %zu\n", sizeof(money.moneystr)); // 100
printf("union大小: %zu\n", sizeof(money)); // 100或104(对齐)
// 数据互斥演示
money.moneyi = 99;
printf("存入整数后: %d\n", money.moneyi);
money.moneyd = 1.23;
printf("存入浮点后: %.2f\n", money.moneyd);
printf("读取整数: %d (错误!数据已覆盖)\n", money.moneyi);
return 0;
}
7.3 共用体应用场景
场景1:节省内存
#include <stdio.h>
// 使用结构体(浪费内存)
struct ProductStruct {
int id;
char name[50];
int intPrice; // 整数价格
double floatPrice; // 浮点价格
// 但每次只用一种价格
}; // 约64字节
// 使用共用体(节省内存)
struct ProductUnion {
int id;
char name[50];
union {
int intPrice;
double floatPrice;
} price;
int priceType; // 0=整数,1=浮点
}; // 约60字节
// 大量数据时节省明显
场景2:类型转换
#include <stdio.h>
union TypeConverter {
float f;
unsigned int i;
};
int main() {
union TypeConverter converter;
converter.f = 3.14f;
printf("浮点数: %f\n", converter.f);
printf("二进制表示: 0x%X\n", converter.i);
// 修改二进制表示
converter.i = 0x40490FDB;
printf("新浮点数: %f\n", converter.f);
return 0;
}
场景3:变体数据类型
#include <stdio.h>
#include <string.h>
typedef enum {
TYPE_INT,
TYPE_FLOAT,
TYPE_STRING
} DataType;
typedef struct {
DataType type;
union {
int i;
float f;
char str[50];
} data;
} Variant;
void printVariant(Variant v) {
switch (v.type) {
case TYPE_INT:
printf("整数: %d\n", v.data.i);
break;
case TYPE_FLOAT:
printf("浮点: %.2f\n", v.data.f);
break;
case TYPE_STRING:
printf("字符串: %s\n", v.data.str);
break;
}
}
int main() {
Variant v1 = {TYPE_INT, {.i = 100}};
Variant v2 = {TYPE_FLOAT, {.f = 3.14}};
Variant v3 = {TYPE_STRING, {.str = "Hello"}};
printVariant(v1);
printVariant(v2);
printVariant(v3);
return 0;
}
8、结构体与共用体对比
| 特性 | 结构体 (struct) | 共用体 (union) |
|---|---|---|
| 用途 | 描述事物的多个属性 | 同一数据的多种表示 |
| 内存 | 各成员独立存储 | 所有成员共享内存 |
| 大小 | 所有成员之和(+对齐) | 最大成员大小(+对齐) |
| 赋值 | 可同时给所有成员赋值 | 每次只能给一个成员赋值 |
| 访问 | 所有成员可同时访问 | 最后赋值的成员有效 |
| 地址 | 各成员地址不同 | 所有成员地址相同 |
| 应用 | 记录、配置、实体 | 类型转换、节省内存 |
9、综合练习
9.1 学生管理系统
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_STUDENTS 100
typedef struct {
int id;
char name[50];
int age;
char gender;
float scores[3]; // 语数英
float total;
float average;
} Student;
// 计算总分和平均分
void calculateStats(Student *s) {
s->total = 0;
for (int i = 0; i < 3; i++) {
s->total += s->scores[i];
}
s->average = s->total / 3;
}
// 添加学生
int addStudent(Student students[], int count) {
if (count >= MAX_STUDENTS) {
printf("学生数量已达上限!\n");
return count;
}
Student *s = &students[count];
printf("请输入学号: ");
scanf("%d", &s->id);
printf("请输入姓名: ");
scanf("%s", s->name);
printf("请输入年龄: ");
scanf("%d", &s->age);
printf("请输入性别 (M/F): ");
scanf(" %c", &s->gender);
printf("请输入语文成绩: ");
scanf("%f", &s->scores[0]);
printf("请输入数学成绩: ");
scanf("%f", &s->scores[1]);
printf("请输入英语成绩: ");
scanf("%f", &s->scores[2]);
calculateStats(s);
return count + 1;
}
// 显示所有学生
void displayStudents(Student students[], int count) {
if (count == 0) {
printf("暂无学生记录\n");
return;
}
printf("\n%-8s %-15s %-5s %-5s %-8s %-8s %-8s %-8s %-8s\n",
"学号", "姓名", "年龄", "性别", "语文", "数学", "英语", "总分", "平均分");
printf("--------------------------------------------------------------------------------\n");
for (int i = 0; i < count; i++) {
printf("%-8d %-15s %-5d %-5c %-8.1f %-8.1f %-8.1f %-8.1f %-8.1f\n",
students[i].id, students[i].name, students[i].age, students[i].gender,
students[i].scores[0], students[i].scores[1], students[i].scores[2],
students[i].total, students[i].average);
}
}
// 查找学生
int findStudent(Student students[], int count, int id) {
for (int i = 0; i < count; i++) {
if (students[i].id == id) {
return i;
}
}
return -1;
}
// 排序(按平均分降序)
void sortByAverage(Student students[], int count) {
for (int i = 0; i < count - 1; i++) {
for (int j = 0; j < count - 1 - i; j++) {
if (students[j].average < students[j + 1].average) {
Student temp = students[j];
students[j] = students[j + 1];
students[j + 1] = temp;
}
}
}
}
int main() {
Student students[MAX_STUDENTS];
int count = 0;
int choice;
while (1) {
printf("\n=== 学生管理系统 ===\n");
printf("1. 添加学生\n");
printf("2. 显示所有学生\n");
printf("3. 查找学生\n");
printf("4. 按平均分排序\n");
printf("5. 退出\n");
printf("请选择: ");
scanf("%d", &choice);
switch (choice) {
case 1:
count = addStudent(students, count);
break;
case 2:
displayStudents(students, count);
break;
case 3: {
int id;
printf("请输入学号: ");
scanf("%d", &id);
int index = findStudent(students, count, id);
if (index >= 0) {
printf("找到学生: %s\n", students[index].name);
} else {
printf("未找到该学生\n");
}
break;
}
case 4:
sortByAverage(students, count);
printf("排序完成!\n");
displayStudents(students, count);
break;
case 5:
printf("再见!\n");
return 0;
default:
printf("无效选择\n");
}
}
}
9.2 配置文件解析器
#include <stdio.h>
#include <string.h>
typedef enum {
CONFIG_INT,
CONFIG_FLOAT,
CONFIG_STRING,
CONFIG_BOOL
} ConfigType;
typedef struct {
char key[50];
ConfigType type;
union {
int i;
float f;
char str[100];
int b;
} value;
} ConfigItem;
void printConfig(ConfigItem config) {
printf("%s = ", config.key);
switch (config.type) {
case CONFIG_INT:
printf("%d (整数)\n", config.value.i);
break;
case CONFIG_FLOAT:
printf("%.2f (浮点)\n", config.value.f);
break;
case CONFIG_STRING:
printf("%s (字符串)\n", config.value.str);
break;
case CONFIG_BOOL:
printf("%s (布尔)\n", config.value.b ? "true" : "false");
break;
}
}
int main() {
ConfigItem configs[] = {
{"port", CONFIG_INT, {.i = 8080}},
{"version", CONFIG_FLOAT, {.f = 1.5}},
{"name", CONFIG_STRING, {"MyApp"}},
{"debug", CONFIG_BOOL, {.b = 1}}
};
int count = sizeof(configs) / sizeof(configs[0]);
printf("=== 配置信息 ===\n");
for (int i = 0; i < count; i++) {
printConfig(configs[i]);
}
return 0;
}
10、常见错误与避坑指南
| 错误类型 | 错误示例 | 正确写法 |
|---|---|---|
| 忘记分号 | struct Student { int a } |
struct Student { int a; }; |
| 修改常量字符串 | char *s="hi"; strcpy(s,"x"); |
char s[]="hi"; strcpy(s,"x"); |
| 结构体比较 | if(s1==s2) |
memcmp或逐成员比较 |
| 返回局部地址 | return &localStruct; |
使用malloc或返回值 |
| 未初始化 | 直接使用结构体 | struct S s = {0}; |
| 嵌套赋值错误 | s.contact = "123" |
strcpy(s.contact.phone, "123") |
| 指针未检查 | p->member |
if(p!=NULL) p->member |
| 内存对齐忽视 | 随意排列成员 | 按大小排序成员 |
| 共用体误用 | 同时访问多个成员 | 只访问最后赋值的成员 |
| typedef位置 | 函数内typedef | 全局定义 |
11、最佳实践总结
✅ 应该做的:
1. 结构体定义放在全局(头文件中)
2. 使用typedef简化类型名
3. 大结构体传指针(const表示只读)
4. 成员按大小排序优化内存
5. 初始化结构体为{0}
6. 使用->访问指针结构体成员
7. 共用体配合类型标记使用
❌ 不应该做的:
1. 不要在函数内定义结构体类型
2. 不要直接比较结构体(用==)
3. 不要返回局部结构体地址
4. 不要忽视内存对齐影响
5. 不要同时访问共用体多个成员
6. 不要忘记释放动态分配的结构体