C 语言实例 – 使用结构体(struct)(长文讲解)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观
结构体的基本语法
什么是结构体?
在C语言中,结构体(struct) 是一种用户自定义的数据类型,允许将不同类型的数据组合成一个整体。可以将其想象为一个“工具箱”,每个工具(数据成员)都有自己的用途,但都被装在同一个箱子里方便携带。例如,记录学生信息时,姓名、年龄、成绩等不同属性可以通过结构体整合为一个统一的数据单元。
定义结构体类型
定义结构体需要使用 struct
关键字,并通过大括号 {}
包含成员变量。例如:
struct Student {
char name[50];
int age;
float score;
};
这里的 Student
是自定义的结构体类型名,后续可通过它声明变量。
声明结构体变量
定义结构体类型后,可通过以下方式声明变量:
struct Student stu1;
struct Student stu2;
也可以在定义结构体的同时声明变量:
struct Student {
char name[50];
int age;
float score;
} stu1, stu2;
这类似于“先设计图纸,再按图纸造房子”的过程。
结构体的初始化与内存分配
初始化结构体变量
结构体变量的初始化有两种方式:
方式一:逐个赋值
struct Student stu;
strcpy(stu.name, "Alice");
stu.age = 20;
stu.score = 90.5;
方式二:直接初始化
struct Student stu = { "Bob", 19, 85.0 };
注意:初始化时需按成员定义的顺序赋值,且字符串需要用双引号包裹。
结构体的内存分配
结构体变量在内存中占据连续的空间,其大小是所有成员占用空间的总和。例如:
| 成员类型 | 占用字节 |
|----------|----------|
| char[50]
| 50 |
| int
| 4 |
| float
| 4 |
| 总计 | 58 |
可以通过 sizeof
运算符验证:
printf("结构体大小:%zu 字节\n", sizeof(struct Student)); // 输出 58
这类似于将多个物品装入一个箱子,总容量是所有物品体积之和。
结构体的嵌套与指针
嵌套结构体
结构体可以包含另一个结构体作为成员,形成嵌套。例如:
struct Date {
int day;
int month;
int year;
};
struct Employee {
char name[50];
struct Date birth_date;
float salary;
};
访问嵌套成员时需用两个点操作符:
struct Employee emp;
emp.birth_date.day = 15;
结构体指针
结构体指针指向结构体变量的首地址,可通过 ->
操作符访问成员:
struct Student *ptr = &stu;
ptr->age = 21; // 等价于 (*ptr).age = 21
指针操作类似于“地图上的标记点”,通过它可以直接定位并修改数据。
结构体与函数
将结构体传递给函数
可以通过指针或直接传递结构体变量:
方法一:传递指针
void update_age(struct Student *stu, int new_age) {
stu->age = new_age;
}
调用时:
update_age(&stu, 22);
方法二:传递结构体变量(按值传递)
void print_info(struct Student stu) {
printf("Name: %s, Age: %d\n", stu.name, stu.age);
}
调用时:
print_info(stu); // 会复制整个结构体到函数参数中
注意:按值传递会复制数据,可能影响性能,尤其当结构体较大时。
动态内存管理
使用 malloc
分配结构体
当需要动态创建结构体时,可通过 malloc
分配内存:
struct Student *dynamic_stu = (struct Student *)malloc(sizeof(struct Student));
if (dynamic_stu != NULL) {
strcpy(dynamic_stu->name, "Charlie");
free(dynamic_stu); // 使用后释放内存
}
这类似于“按需租用仓库”,用完后归还以避免内存泄漏。
实际案例:学生信息管理系统
案例需求
设计一个程序,实现以下功能:
- 添加学生信息;
- 显示所有学生;
- 按年龄排序学生列表。
代码实现
定义结构体
struct Student {
char name[50];
int age;
float score;
};
主函数逻辑
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_STUDENTS 100
int main() {
struct Student students[MAX_STUDENTS];
int count = 0;
while (1) {
printf("\n1. 添加学生 2. 显示学生 3. 退出\n");
int choice;
scanf("%d", &choice);
switch (choice) {
case 1:
if (count < MAX_STUDENTS) {
struct Student new_stu;
printf("输入姓名:");
scanf("%s", new_stu.name);
printf("输入年龄:");
scanf("%d", &new_stu.age);
students[count++] = new_stu;
} else {
printf("学生数量已达上限!\n");
}
break;
case 2:
for (int i = 0; i < count; i++) {
printf("姓名:%s 年龄:%d\n",
students[i].name, students[i].age);
}
break;
case 3:
return 0;
default:
printf("无效选项!\n");
}
}
return 0;
}
扩展功能:按年龄排序
通过 qsort
函数实现排序:
#include <stdlib.h>
int compare_age(const void *a, const void *b) {
struct Student *stu_a = (struct Student *)a;
struct Student *stu_b = (struct Student *)b;
return stu_a->age - stu_b->age;
}
// 在主函数中添加排序逻辑:
qsort(students, count, sizeof(struct Student), compare_age);
结构体的进阶应用
结构体与文件操作
结构体数据可序列化后存入文件。例如:
// 写入文件
FILE *fp = fopen("students.dat", "wb");
fwrite(students, sizeof(struct Student), count, fp);
fclose(fp);
// 读取文件
fp = fopen("students.dat", "rb");
fread(students, sizeof(struct Student), MAX_STUDENTS, fp);
fclose(fp);
联合体(Union)与结构体对比
结构体和联合体的区别在于内存分配:
- 结构体:成员各自占用独立内存;
- 联合体:所有成员共享同一块内存。
例如:
union Data {
int num;
float fnum;
char str[20];
};
联合体适合存储不同类型但无需同时存在的数据,类似于“抽屉”中只能放一个物品。
结论
通过本文的讲解,读者可以掌握 C 语言实例 – 使用结构体(struct) 的核心概念和应用场景。结构体不仅是数据组织的强大工具,更是理解复杂数据结构(如链表、树)的基础。建议读者通过实际编写学生管理系统、图书信息库等项目,进一步巩固知识。随着学习的深入,可以探索结构体与指针、动态内存分配的结合,以及在实际工程中的高级应用。
掌握结构体后,您将能够更高效地管理复杂数据,并为后续学习C语言中的高级主题(如文件操作、数据结构)打下坚实基础。