MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

C语言结构体数组的使用方法

2024-02-064.3k 阅读

结构体数组的基本概念

结构体的定义回顾

在深入探讨结构体数组之前,让我们先回顾一下结构体的定义。结构体是一种用户自定义的数据类型,它允许将不同类型的数据组合在一起,形成一个逻辑上相关的整体。例如,我们要描述一个学生的信息,可能包含姓名(字符串)、年龄(整数)和成绩(浮点数),可以这样定义结构体:

struct Student {
    char name[50];
    int age;
    float score;
};

这里,struct Student 就是我们定义的结构体类型,其中 nameagescore 被称为结构体的成员。

结构体数组的定义

结构体数组,简单来说,就是数组中的每个元素都是一个结构体类型。当我们需要处理多个具有相同结构体类型的对象时,使用结构体数组就非常方便。比如,我们要管理一个班级学生的信息,就可以定义一个 Student 结构体的数组。定义结构体数组的语法如下:

struct Student students[30];

这里定义了一个名为 students 的结构体数组,它可以容纳 30 个 struct Student 类型的元素,也就是可以存储 30 个学生的信息。

结构体数组的初始化

逐个元素初始化

就像普通数组一样,结构体数组也可以在定义时进行初始化。我们可以逐个初始化数组中的每个结构体元素。以之前定义的 Student 结构体数组为例:

struct Student students[3] = {
    {"Alice", 20, 85.5},
    {"Bob", 21, 90.0},
    {"Charlie", 20, 78.0}
};

在这个初始化列表中,每个花括号内的值对应着 struct Student 结构体的成员顺序,依次初始化每个结构体元素。

部分初始化

如果只希望初始化数组中的部分元素,也是可行的。未初始化的元素会被自动初始化为相应类型的默认值(对于基本数据类型,如整数为 0,浮点数为 0.0,字符数组会被初始化为空字符串)。例如:

struct Student students[3] = {
    {"Alice", 20, 85.5}
};

这里只初始化了 students[0]students[1]students[2] 会按照默认值初始化。

动态初始化

在程序运行过程中,我们也可以动态地初始化结构体数组。通过用户输入或者从文件中读取数据来填充结构体数组。以下是一个通过用户输入来初始化结构体数组的示例:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float score;
};

int main() {
    struct Student students[3];
    for (int i = 0; i < 3; i++) {
        printf("请输入第 %d 个学生的姓名: ", i + 1);
        scanf("%s", students[i].name);
        printf("请输入第 %d 个学生的年龄: ", i + 1);
        scanf("%d", &students[i].age);
        printf("请输入第 %d 个学生的成绩: ", i + 1);
        scanf("%f", &students[i].score);
    }
    return 0;
}

在这个程序中,通过 for 循环依次提示用户输入每个学生的信息,从而动态地初始化了结构体数组 students

访问结构体数组元素及成员

通过下标访问数组元素

结构体数组的元素访问和普通数组类似,使用下标运算符 []。例如,要访问 students 数组中的第二个学生(数组下标从 0 开始,所以第二个学生下标为 1),可以这样写:

struct Student secondStudent = students[1];

这里将 students 数组中第二个结构体元素赋值给了 secondStudent 变量。

访问结构体成员

一旦获取了结构体数组中的某个元素,就可以像访问普通结构体变量的成员一样,使用点运算符 . 来访问其成员。例如,要获取上述 secondStudent 的年龄,可以这样:

int age = secondStudent.age;

如果直接通过结构体数组访问成员,写法如下:

int age = students[1].age;

这直接获取了 students 数组中第二个学生的年龄。

嵌套结构体数组的访问

当结构体成员本身又是一个结构体,并且包含数组时,访问会稍微复杂一些。例如,我们定义一个包含地址结构体的 Person 结构体,地址结构体又包含街道数组:

struct Address {
    char street[100];
    int zipCode;
};

struct Person {
    char name[50];
    struct Address addresses[3];
};

现在,如果有一个 Person 结构体数组 people,要访问第一个人的第二个地址的街道名称,可以这样:

struct Person people[10];
// 假设已经初始化了 people 数组
char *street = people[0].addresses[1].street;

结构体数组的遍历

使用 for 循环遍历

最常见的遍历结构体数组的方式是使用 for 循环。以 Student 结构体数组为例,遍历打印每个学生的信息:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float score;
};

int main() {
    struct Student students[3] = {
        {"Alice", 20, 85.5},
        {"Bob", 21, 90.0},
        {"Charlie", 20, 78.0}
    };
    for (int i = 0; i < 3; i++) {
        printf("学生姓名: %s, 年龄: %d, 成绩: %.2f\n", students[i].name, students[i].age, students[i].score);
    }
    return 0;
}

在这个 for 循环中,每次迭代通过 i 下标获取 students 数组中的一个结构体元素,并打印出其成员信息。

使用 while 循环遍历

while 循环也可以用于遍历结构体数组。以下是用 while 循环实现相同功能的代码:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float score;
};

int main() {
    struct Student students[3] = {
        {"Alice", 20, 85.5},
        {"Bob", 21, 90.0},
        {"Charlie", 20, 78.0}
    };
    int i = 0;
    while (i < 3) {
        printf("学生姓名: %s, 年龄: %d, 成绩: %.2f\n", students[i].name, students[i].age, students[i].score);
        i++;
    }
    return 0;
}

这里通过 while 循环,在条件 i < 3 满足时,不断访问结构体数组元素并打印信息,同时 i 自增以移动到下一个元素。

结构体数组的排序

按单一成员排序

在实际应用中,经常需要对结构体数组进行排序。假设我们要按照学生的成绩对 students 数组进行升序排序,可以使用冒泡排序算法。以下是实现代码:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float score;
};

void swap(struct Student *a, struct Student *b) {
    struct Student temp = *a;
    *a = *b;
    *b = temp;
}

void sortByScore(struct Student students[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (students[j].score > students[j + 1].score) {
                swap(&students[j], &students[j + 1]);
            }
        }
    }
}

int main() {
    struct Student students[3] = {
        {"Alice", 20, 85.5},
        {"Bob", 21, 90.0},
        {"Charlie", 20, 78.0}
    };
    sortByScore(students, 3);
    for (int i = 0; i < 3; i++) {
        printf("学生姓名: %s, 年龄: %d, 成绩: %.2f\n", students[i].name, students[i].age, students[i].score);
    }
    return 0;
}

在这个代码中,swap 函数用于交换两个结构体元素,sortByScore 函数实现了冒泡排序逻辑,根据学生的成绩对结构体数组进行排序。

按多个成员排序

有时候可能需要按多个成员进行排序。例如,先按成绩降序排序,如果成绩相同,再按年龄升序排序。以下是实现代码:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float score;
};

void swap(struct Student *a, struct Student *b) {
    struct Student temp = *a;
    *a = *b;
    *b = temp;
}

void sortByScoreAndAge(struct Student students[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (students[j].score < students[j + 1].score || 
                (students[j].score == students[j + 1].score && students[j].age > students[j + 1].age)) {
                swap(&students[j], &students[j + 1]);
            }
        }
    }
}

int main() {
    struct Student students[3] = {
        {"Alice", 20, 85.5},
        {"Bob", 21, 85.5},
        {"Charlie", 20, 78.0}
    };
    sortByScoreAndAge(students, 3);
    for (int i = 0; i < 3; i++) {
        printf("学生姓名: %s, 年龄: %d, 成绩: %.2f\n", students[i].name, students[i].age, students[i].score);
    }
    return 0;
}

sortByScoreAndAge 函数中,比较条件首先判断成绩,如果成绩相同再比较年龄,从而实现了按多个成员排序。

结构体数组与函数

传递结构体数组到函数

在编程中,经常需要将结构体数组传递给函数进行处理。例如,我们有一个函数用于计算学生的平均成绩,该函数接收一个 Student 结构体数组和数组的大小作为参数:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float score;
};

float calculateAverage(struct Student students[], int n) {
    float total = 0;
    for (int i = 0; i < n; i++) {
        total += students[i].score;
    }
    return total / n;
}

int main() {
    struct Student students[3] = {
        {"Alice", 20, 85.5},
        {"Bob", 21, 90.0},
        {"Charlie", 20, 78.0}
    };
    float average = calculateAverage(students, 3);
    printf("学生的平均成绩为: %.2f\n", average);
    return 0;
}

在这个例子中,calculateAverage 函数接收 students 结构体数组和数组大小 n,通过遍历数组计算总成绩并返回平均成绩。

函数返回结构体数组

函数也可以返回结构体数组。不过需要注意的是,由于函数的局部变量在函数结束时会被销毁,如果要返回结构体数组,通常需要返回指向动态分配内存的指针。以下是一个简单示例,函数创建并返回一个包含两个学生信息的结构体数组:

#include <stdio.h>
#include <stdlib.h>

struct Student {
    char name[50];
    int age;
    float score;
};

struct Student* createStudents() {
    struct Student *students = (struct Student*)malloc(2 * sizeof(struct Student));
    if (students == NULL) {
        printf("内存分配失败\n");
        exit(1);
    }
    students[0] = (struct Student){"Alice", 20, 85.5};
    students[1] = (struct Student){"Bob", 21, 90.0};
    return students;
}

int main() {
    struct Student *students = createStudents();
    for (int i = 0; i < 2; i++) {
        printf("学生姓名: %s, 年龄: %d, 成绩: %.2f\n", students[i].name, students[i].age, students[i].score);
    }
    free(students);
    return 0;
}

createStudents 函数中,使用 malloc 动态分配内存来存储结构体数组,然后填充数据并返回指针。在 main 函数中,获取返回的指针并使用,最后记得使用 free 释放内存,以避免内存泄漏。

结构体数组与文件操作

将结构体数组写入文件

在实际应用中,经常需要将结构体数组的数据保存到文件中,以便后续使用。以下是将 Student 结构体数组写入二进制文件的示例:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float score;
};

int main() {
    struct Student students[3] = {
        {"Alice", 20, 85.5},
        {"Bob", 21, 90.0},
        {"Charlie", 20, 78.0}
    };
    FILE *file = fopen("students.bin", "wb");
    if (file == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
    fwrite(students, sizeof(struct Student), 3, file);
    fclose(file);
    return 0;
}

在这个代码中,使用 fopen 以二进制写入模式打开文件 students.bin,然后使用 fwrite 函数将 students 结构体数组的内容写入文件,最后关闭文件。

从文件读取结构体数组

读取之前保存到文件中的结构体数组数据也是很常见的操作。以下是从二进制文件读取 Student 结构体数组的示例:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float score;
};

int main() {
    struct Student students[3];
    FILE *file = fopen("students.bin", "rb");
    if (file == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
    fread(students, sizeof(struct Student), 3, file);
    fclose(file);
    for (int i = 0; i < 3; i++) {
        printf("学生姓名: %s, 年龄: %d, 成绩: %.2f\n", students[i].name, students[i].age, students[i].score);
    }
    return 0;
}

这里使用 fopen 以二进制读取模式打开文件,然后通过 fread 函数将文件内容读取到 students 结构体数组中,最后遍历并打印数组内容。

文本文件操作

除了二进制文件,也可以使用文本文件来存储和读取结构体数组。以下是将 Student 结构体数组写入文本文件的示例:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float score;
};

int main() {
    struct Student students[3] = {
        {"Alice", 20, 85.5},
        {"Bob", 21, 90.0},
        {"Charlie", 20, 78.0}
    };
    FILE *file = fopen("students.txt", "w");
    if (file == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
    for (int i = 0; i < 3; i++) {
        fprintf(file, "%s %d %.2f\n", students[i].name, students[i].age, students[i].score);
    }
    fclose(file);
    return 0;
}

在这个代码中,使用 fprintf 按格式将每个结构体元素的成员写入文本文件。读取文本文件并填充结构体数组的示例如下:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float score;
};

int main() {
    struct Student students[3];
    FILE *file = fopen("students.txt", "r");
    if (file == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
    for (int i = 0; i < 3; i++) {
        fscanf(file, "%s %d %f", students[i].name, &students[i].age, &students[i].score);
    }
    fclose(file);
    for (int i = 0; i < 3; i++) {
        printf("学生姓名: %s, 年龄: %d, 成绩: %.2f\n", students[i].name, students[i].age, students[i].score);
    }
    return 0;
}

这里通过 fscanf 从文本文件中按格式读取数据并填充结构体数组。

结构体数组与指针

指向结构体数组的指针

在 C 语言中,可以定义一个指针指向结构体数组。例如,对于 Student 结构体数组 students,可以这样定义指针:

struct Student students[3];
struct Student *ptr = students;

这里 ptr 是一个指向 students 数组首元素的指针。通过指针可以像使用数组名一样访问结构体数组元素及其成员。例如:

printf("学生姓名: %s, 年龄: %d, 成绩: %.2f\n", ptr->name, ptr->age, ptr->score);

这里使用 -> 运算符通过指针访问结构体成员,它等价于 (*ptr).name(*ptr).age(*ptr).score

动态分配结构体数组指针

可以使用 malloc 动态分配结构体数组的内存,并通过指针来操作。以下是一个示例:

#include <stdio.h>
#include <stdlib.h>

struct Student {
    char name[50];
    int age;
    float score;
};

int main() {
    int n = 3;
    struct Student *students = (struct Student*)malloc(n * sizeof(struct Student));
    if (students == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    for (int i = 0; i < n; i++) {
        printf("请输入第 %d 个学生的姓名: ", i + 1);
        scanf("%s", students[i].name);
        printf("请输入第 %d 个学生的年龄: ", i + 1);
        scanf("%d", &students[i].age);
        printf("请输入第 %d 个学生的成绩: ", i + 1);
        scanf("%f", &students[i].score);
    }
    for (int i = 0; i < n; i++) {
        printf("学生姓名: %s, 年龄: %d, 成绩: %.2f\n", students[i].name, students[i].age, students[i].score);
    }
    free(students);
    return 0;
}

在这个代码中,使用 malloc 分配了能容纳 nStudent 结构体的内存,并通过指针 students 进行操作。在使用完毕后,记得使用 free 释放内存。

结构体数组中指针成员的使用

结构体本身也可以包含指针成员。例如,我们修改 Student 结构体,使其包含一个指向字符串的指针成员:

#include <stdio.h>
#include <stdlib.h>

struct Student {
    char *name;
    int age;
    float score;
};

int main() {
    struct Student students[3];
    for (int i = 0; i < 3; i++) {
        students[i].name = (char*)malloc(50 * sizeof(char));
        if (students[i].name == NULL) {
            printf("内存分配失败\n");
            return 1;
        }
        printf("请输入第 %d 个学生的姓名: ", i + 1);
        scanf("%s", students[i].name);
        printf("请输入第 %d 个学生的年龄: ", i + 1);
        scanf("%d", &students[i].age);
        printf("请输入第 %d 个学生的成绩: ", i + 1);
        scanf("%f", &students[i].score);
    }
    for (int i = 0; i < 3; i++) {
        printf("学生姓名: %s, 年龄: %d, 成绩: %.2f\n", students[i].name, students[i].age, students[i].score);
        free(students[i].name);
    }
    return 0;
}

在这个代码中,每个 Student 结构体元素的 name 成员是一个指针,需要动态分配内存来存储字符串。在使用完毕后,记得释放每个 name 指针所指向的内存,以避免内存泄漏。

结构体数组在实际项目中的应用场景

数据库模拟

在一些小型项目中,可能没有使用专业的数据库管理系统,而是通过结构体数组来模拟简单的数据库。例如,一个小型的图书管理系统,可以使用结构体数组来存储图书信息,包括书名、作者、出版年份等。每个结构体元素代表一本图书,通过对结构体数组的操作,如添加、删除、查询图书信息,来实现数据库的基本功能。

游戏开发

在游戏开发中,结构体数组可用于管理游戏中的对象。比如一款角色扮演游戏,每个角色都有自己的属性,如生命值、攻击力、防御力等。可以定义一个结构体来描述角色属性,然后使用结构体数组来管理游戏中的所有角色。通过对结构体数组的遍历和操作,可以实现角色的生成、移动、攻击等游戏逻辑。

图形界面开发

在图形界面开发中,结构体数组可用于管理界面元素。例如,一个简单的窗口程序,窗口中可能包含多个按钮、文本框等控件。可以定义一个结构体来描述每个控件的属性,如位置、大小、文本内容等,然后使用结构体数组来管理所有控件。通过对结构体数组的操作,可以实现控件的绘制、事件处理等功能。

数据统计与分析

在数据统计与分析的应用中,结构体数组可用于存储和处理数据。例如,统计一个班级学生的考试成绩,需要记录每个学生的各科成绩、总分、平均分等信息。可以定义一个结构体来描述学生的成绩信息,使用结构体数组来存储所有学生的数据。通过对结构体数组的计算和分析,可以得出班级的平均成绩、最高分、最低分等统计结果。

总之,结构体数组在 C 语言编程中是一个非常强大和实用的工具,通过合理地运用结构体数组,可以有效地组织和管理复杂的数据,实现各种功能丰富的应用程序。无论是小型的控制台程序,还是大型的商业项目,结构体数组都能发挥重要的作用。在实际编程过程中,需要根据具体的需求,灵活地使用结构体数组的各种特性,以提高程序的效率和可维护性。同时,要注意内存管理,避免内存泄漏等问题,确保程序的稳定性和可靠性。