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); // 使用后释放内存  
}  

这类似于“按需租用仓库”,用完后归还以避免内存泄漏。


实际案例:学生信息管理系统

案例需求

设计一个程序,实现以下功能:

  1. 添加学生信息;
  2. 显示所有学生;
  3. 按年龄排序学生列表。

代码实现

定义结构体

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语言中的高级主题(如文件操作、数据结构)打下坚实基础。

最新发布