C 结构体(建议收藏)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

结构体的基本概念与核心作用

在 C 语言中,结构体(Struct)是一种用户自定义的数据类型,它允许开发者将不同类型的数据组合成一个有意义的整体。例如,一个快递包裹通常包含收件人姓名、电话号码、地址、包裹重量等信息,这些信息的集合就可以通过结构体来表示。结构体的出现,解决了传统数组或变量分散存储的弊端,使数据组织更加直观高效。

结构体与数组有本质区别:数组中的元素类型必须相同,而结构体的成员可以是不同数据类型。例如,一个学生信息的结构体可能包含整型的学号、字符型的姓名、浮点型的平均分等。这种灵活性使得结构体成为 C 语言中处理复杂数据结构的核心工具。

如何定义与使用结构体

定义结构体类型

定义结构体需要使用 struct 关键字,并通过花括号 {} 将成员变量包裹起来。例如,定义一个表示书籍信息的结构体:

struct Book {  
    char title[50];  
    char author[30];  
    float price;  
    int pages;  
};  

创建结构体变量

定义结构体类型后,可以通过两种方式创建变量:

  1. 直接声明
    struct Book book1;  
    
  2. 使用 typedef 简化语法
    typedef struct {  
        char title[50];  
        char author[30];  
        float price;  
        int pages;  
    } Book;  
    Book book2;  // 直接使用 Book 作为类型名  
    

    typedef 的作用是为结构体创建一个别名,使代码更简洁。

访问结构体成员

通过 . 运算符可以访问结构体成员:

book1.title = "C程序设计语言";  
book1.price = 79.99;  
printf("书名:%s,价格:%.2f 元\n", book1.title, book1.price);  

实例:学生信息管理

假设要管理学生信息,可以定义如下结构体:

typedef struct {  
    int id;  
    char name[20];  
    float gpa;  
} Student;  

int main() {  
    Student student1;  
    student1.id = 1001;  
    strcpy(student1.name, "张三");  
    student1.gpa = 3.8;  
    printf("学号:%d,姓名:%s,GPA:%.1f\n", student1.id, student1.name, student1.gpa);  
    return 0;  
}  

结构体的内存管理与特性

内存分配原理

每个结构体变量在内存中占用的大小,等于其所有成员所占空间的总和(可能经过对齐调整)。例如,若一个结构体包含 int(4字节)、char(1字节)和 float(4字节),则总大小为 4 + 1 + 4 = 9 字节(忽略对齐影响)。

内存对齐与填充

为提高访问效率,编译器会自动对结构体成员进行对齐。例如,假设 int 类型的对齐要求为4字节,则以下结构体:

struct AlignTest {  
    char a;    // 1字节  
    int b;     // 4字节  
    char c;    // 1字节  
};  

实际占用的内存可能是 1 + 3(填充) + 4 + 1 + 3(填充) = 12 字节。填充机制确保成员地址是其类型的倍数,但会增加内存开销。开发者可通过 #pragma pack 指令调整对齐方式。

结构体数组与指针

结构体数组

可以创建结构体数组来存储多个对象。例如:

Student students[3];  
students[0].id = 1001;  
strcpy(students[1].name, "李四");  

结构体指针

通过指针访问结构体成员时,需使用 -> 运算符:

Student *p = &student1;  
p->gpa = 3.9;  

动态内存分配

使用 malloccalloc 可动态分配结构体内存:

Student *pDynamic = (Student *)malloc(sizeof(Student));  
if (pDynamic != NULL) {  
    pDynamic->id = 1002;  
    free(pDynamic);  
}  

结构体的高级应用场景

结构体嵌套

结构体可以包含其他结构体作为成员,实现更复杂的数据组织。例如:

typedef struct {  
    int year;  
    int month;  
    int day;  
} Date;  

typedef struct {  
    char name[30];  
    Date birth_date;  // 嵌套 Date 结构体  
} Employee;  

结构体与函数

结构体变量可作为函数参数或返回值传递。例如:

void printStudent(Student s) {  
    printf("学号:%d,姓名:%s\n", s.id, s.name);  
}  

Student createStudent(int id, const char *name) {  
    Student s = {id, name, 0.0};  
    return s;  
}  

链表的实现

链表是结构体的经典应用,每个节点包含数据和指向下一个节点的指针:

typedef struct Node {  
    int data;  
    struct Node *next;  // 自引用指针  
} Node;  

void insertNode(Node **head, int value) {  
    Node *newNode = (Node *)malloc(sizeof(Node));  
    newNode->data = value;  
    newNode->next = *head;  
    *head = newNode;  
}  

实战案例:学生管理系统

以下是一个完整的案例,展示结构体在学生信息管理中的应用:

#include <stdio.h>  
#include <string.h>  

typedef struct {  
    int id;  
    char name[20];  
    float gpa;  
} Student;  

void addStudent(Student *students, int *count) {  
    if (*count >= 100) {  
        printf("已达到最大存储量!\n");  
        return;  
    }  
    printf("输入学号:");  
    scanf("%d", &students[*count].id);  
    printf("输入姓名:");  
    scanf("%s", students[*count].name);  
    printf("输入GPA:");  
    scanf("%f", &students[*count].gpa);  
    (*count)++;  
}  

void displayStudents(Student *students, int count) {  
    printf("\n当前学生信息:\n");  
    for (int i = 0; i < count; i++) {  
        printf("学号:%d,姓名:%s,GPA:%.1f\n",  
               students[i].id, students[i].name, students[i].gpa);  
    }  
}  

int main() {  
    Student students[100];  
    int count = 0;  
    int choice;  

    do {  
        printf("\n学生管理系统\n");  
        printf("1. 添加学生\n2. 显示学生\n3. 退出\n");  
        printf("请选择:");  
        scanf("%d", &choice);  

        switch (choice) {  
            case 1:  
                addStudent(students, &count);  
                break;  
            case 2:  
                displayStudents(students, count);  
                break;  
            case 3:  
                printf("退出系统。\n");  
                break;  
            default:  
                printf("无效选项!\n");  
        }  
    } while (choice != 3);  

    return 0;  
}  

结构体的常见问题与最佳实践

问题1:结构体成员访问越界

若未正确初始化结构体数组,可能导致越界访问。例如,声明 Student students[5]; 后,若 count 超过5,就会引发错误。

问题2:内存泄漏

在使用动态分配的结构体时,务必通过 free() 释放内存,避免内存泄漏。

最佳实践建议

  1. 使用 typedef 简化代码,减少冗余的 struct 关键字。
  2. 对于大型结构体,考虑使用指针传递以避免拷贝开销。
  3. 初始化结构体时,可采用指定初始化语法:
    Student s = {.id = 1001, .name = "王五", .gpa = 3.7};  
    

结论

C 结构体通过灵活的数据组合能力,解决了传统数据类型无法满足的复杂场景需求。无论是管理学生信息、实现链表,还是嵌套复杂数据结构,结构体都提供了高效且直观的解决方案。对于开发者而言,掌握结构体的定义、内存管理、指针操作等核心知识点,是深入理解 C 语言编程的关键。

通过实际案例的演练,读者可以逐步掌握结构体的应用技巧,最终将其融入到更复杂的项目开发中。结构体不仅是数据组织的工具,更是 C 语言面向对象编程思想的雏形,值得开发者深入探索。

最新发布