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

C语言结构体数组的创建与操作

2023-07-174.2k 阅读

C语言结构体数组的创建

在C语言中,结构体是一种用户自定义的数据类型,它允许将不同类型的数据组合在一起。而结构体数组则是由多个结构体变量组成的数组。创建结构体数组为处理一组相关的数据提供了一种便捷的方式。

结构体定义基础

在创建结构体数组之前,首先需要定义结构体类型。结构体定义使用struct关键字,其一般形式如下:

struct 结构体名 {
    数据类型 成员1;
    数据类型 成员2;
    // 可以有更多的成员
    数据类型 成员n;
};

例如,我们定义一个表示学生信息的结构体:

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

这里定义了一个名为Student的结构体,它包含三个成员:一个字符数组name用于存储学生姓名,一个整型age用于存储年龄,以及一个浮点型score用于存储成绩。

结构体数组的创建方式

  1. 先定义结构体类型,再创建数组
    • 首先定义结构体类型,如上述的Student结构体。然后可以像定义普通数组一样定义结构体数组。例如:
struct Student students[3];

这里创建了一个名为students的结构体数组,它可以容纳3个Student类型的元素。每个元素都是一个完整的Student结构体,拥有nameagescore成员。

  1. 在定义结构体类型的同时创建数组
    • 可以在定义结构体类型的同时声明结构体数组。例如:
struct Student {
    char name[50];
    int age;
    float score;
} students[3];

这种方式和先定义结构体类型再创建数组在功能上是等效的。只是在语法上更加紧凑,适用于只需要使用一次该结构体类型的情况。

  1. 动态分配内存创建结构体数组
    • 使用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;
    }
    // 使用完后记得释放内存
    free(students);
    return 0;
}

在这个例子中,malloc函数分配了足够容纳nStudent结构体的内存空间,并将返回的指针强制转换为struct Student *类型。通过这种方式,可以根据程序运行时的需求动态调整结构体数组的大小。但是要注意,动态分配的内存使用完毕后必须使用free函数释放,以避免内存泄漏。

结构体数组初始化

  1. 逐个初始化元素
    • 对于前面定义的students数组,可以逐个初始化其元素。例如:
struct Student students[3] = {
    {"Alice", 20, 85.5},
    {"Bob", 21, 90.0},
    {"Charlie", 19, 78.0}
};

这里使用花括号{}对每个结构体元素进行初始化,每个元素的初始化值按照结构体成员的顺序依次列出。

  1. 部分初始化
    • 也可以只初始化数组的部分元素,未初始化的元素会被自动初始化为0(对于数值类型)或空字符(对于字符类型)。例如:
struct Student students[3] = {
    {"Alice", 20, 85.5}
};

在这种情况下,students[0]被初始化为{"Alice", 20, 85.5}students[1]students[2]name成员被初始化为空字符串,age成员被初始化为0,score成员被初始化为0.0。

C语言结构体数组的操作

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

  1. 通过数组下标访问
    • 一旦创建并初始化了结构体数组,就可以通过数组下标来访问每个结构体元素,进而访问其成员。例如,要访问students数组中第一个学生的姓名,可以这样写:
#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", 19, 78.0}
    };
    printf("第一个学生的姓名: %s\n", students[0].name);
    return 0;
}

在C语言中,使用点运算符.来访问结构体成员。students[0]表示数组中的第一个结构体元素,students[0].name则表示该元素的name成员。

  1. 通过指针访问
    • 可以使用指针来访问结构体数组元素及其成员。例如:
#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", 19, 78.0}
    };
    struct Student *ptr = students;
    printf("第一个学生的姓名: %s\n", ptr->name);
    return 0;
}

这里定义了一个指向Student结构体的指针ptr,并使其指向students数组的起始地址。在通过指针访问结构体成员时,使用->运算符,ptr->name等价于(*ptr).nameptr->name表示指针ptr所指向的结构体元素的name成员。

遍历结构体数组

  1. 使用for循环遍历
    • 遍历结构体数组是常见的操作,通常使用for循环来实现。例如,要打印出students数组中所有学生的信息,可以这样写:
#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", 19, 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从0遍历到2(因为数组下标从0开始,且数组长度为3)。每次循环通过students[i]获取数组中的第i个结构体元素,并通过点运算符访问其成员,然后进行打印。

  1. 使用指针遍历
    • 同样可以使用指针来遍历结构体数组。例如:
#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", 19, 78.0}
    };
    struct Student *ptr = students;
    for (int i = 0; i < 3; i++) {
        printf("学生姓名: %s, 年龄: %d, 成绩: %.2f\n", ptr->name, ptr->age, ptr->score);
        ptr++;
    }
    return 0;
}

这里使用指针ptr指向students数组的起始地址。在for循环中,通过ptr->成员名的方式访问结构体成员,并在每次循环结束时将指针ptr向后移动一个结构体的大小(通过ptr++),以指向下一个结构体元素。

修改结构体数组元素的成员值

  1. 直接修改
    • 可以直接通过数组下标或指针来修改结构体数组元素的成员值。例如,将students数组中第二个学生的成绩提高5分:
#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", 19, 78.0}
    };
    students[1].score += 5;
    printf("修改后的第二个学生成绩: %.2f\n", students[1].score);
    return 0;
}

在这个例子中,通过students[1].score直接访问并修改了第二个学生的成绩成员值。

  1. 通过函数修改
    • 也可以将结构体数组作为参数传递给函数,在函数中修改其成员值。例如:
#include <stdio.h>
struct Student {
    char name[50];
    int age;
    float score;
};
void increaseScore(struct Student *stu, int index, float increment) {
    stu[index].score += increment;
}
int main() {
    struct Student students[3] = {
        {"Alice", 20, 85.5},
        {"Bob", 21, 90.0},
        {"Charlie", 19, 78.0}
    };
    increaseScore(students, 1, 5);
    printf("修改后的第二个学生成绩: %.2f\n", students[1].score);
    return 0;
}

在这个例子中,定义了一个increaseScore函数,它接受一个结构体指针stu(指向结构体数组的起始地址)、要修改的元素下标index以及成绩增加量increment。在函数内部,通过stu[index].score来修改指定学生的成绩。

结构体数组的排序

  1. 按单个成员排序(以成绩为例)
    • 可以使用常见的排序算法(如冒泡排序、选择排序、插入排序等)对结构体数组进行排序。这里以冒泡排序为例,对students数组按成绩从高到低排序:
#include <stdio.h>
struct Student {
    char name[50];
    int age;
    float score;
};
void bubbleSortByScore(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) {
                struct Student temp = students[j];
                students[j] = students[j + 1];
                students[j + 1] = temp;
            }
        }
    }
}
int main() {
    struct Student students[3] = {
        {"Alice", 20, 85.5},
        {"Bob", 21, 90.0},
        {"Charlie", 19, 78.0}
    };
    bubbleSortByScore(students, 3);
    for (int i = 0; i < 3; i++) {
        printf("学生姓名: %s, 成绩: %.2f\n", students[i].name, students[i].score);
    }
    return 0;
}

bubbleSortByScore函数中,通过嵌套的for循环实现冒泡排序。每次比较相邻两个学生的成绩,如果前一个学生的成绩小于后一个学生的成绩,则交换这两个学生的结构体元素。

  1. 按多个成员排序(如先按年龄,年龄相同按成绩)
    • 当需要按多个成员排序时,在比较函数中需要考虑多个条件。以下是一个按年龄升序,年龄相同按成绩降序排序的例子:
#include <stdio.h>
struct Student {
    char name[50];
    int age;
    float score;
};
void customSort(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].age > students[j + 1].age) {
                struct Student temp = students[j];
                students[j] = students[j + 1];
                students[j + 1] = temp;
            } else if (students[j].age == students[j + 1].age) {
                if (students[j].score < students[j + 1].score) {
                    struct Student temp = students[j];
                    students[j] = students[j + 1];
                    students[j + 1] = temp;
                }
            }
        }
    }
}
int main() {
    struct Student students[3] = {
        {"Alice", 20, 85.5},
        {"Bob", 20, 90.0},
        {"Charlie", 19, 78.0}
    };
    customSort(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;
}

customSort函数中,外层for循环控制排序轮数,内层for循环进行相邻元素比较。首先比较年龄,如果前一个学生年龄大于后一个学生年龄,则交换;如果年龄相同,则进一步比较成绩,若前一个学生成绩小于后一个学生成绩,则交换。

结构体数组与文件操作

  1. 将结构体数组写入文件
    • 可以将结构体数组的内容写入文件,以便长期保存数据。例如,将students数组写入一个二进制文件:
#include <stdio.h>
#include <stdlib.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", 19, 78.0}
    };
    FILE *file = fopen("students.dat", "wb");
    if (file == NULL) {
        perror("打开文件失败");
        return 1;
    }
    fwrite(students, sizeof(struct Student), 3, file);
    fclose(file);
    return 0;
}

在这个例子中,使用fopen函数以二进制写入模式("wb")打开一个名为students.dat的文件。然后使用fwrite函数将students数组的内容写入文件,fwrite函数的第一个参数是要写入的数据的指针(即students数组的起始地址),第二个参数是每个数据项的大小(这里是sizeof(struct Student)),第三个参数是要写入的数据项的数量(这里是3),第四个参数是文件指针file。最后使用fclose函数关闭文件。

  1. 从文件读取结构体数组
    • 从文件中读取之前写入的结构体数组数据。例如:
#include <stdio.h>
#include <stdlib.h>
struct Student {
    char name[50];
    int age;
    float score;
};
int main() {
    struct Student students[3];
    FILE *file = fopen("students.dat", "rb");
    if (file == NULL) {
        perror("打开文件失败");
        return 1;
    }
    size_t result = fread(students, sizeof(struct Student), 3, file);
    if (result != 3) {
        printf("读取数据不完全\n");
    }
    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函数以二进制读取模式("rb")打开students.dat文件。然后使用fread函数从文件中读取数据到students数组,fread函数的参数与fwrite类似。读取完成后,检查读取的数据项数量是否与预期一致(这里预期读取3个结构体元素)。最后关闭文件,并遍历打印出读取到的学生信息。

通过以上对C语言结构体数组的创建与操作的详细介绍,相信读者对结构体数组在C语言编程中的应用有了更深入的理解和掌握。无论是在处理复杂数据结构,还是在进行数据存储与处理等方面,结构体数组都提供了强大而灵活的工具。在实际编程中,可以根据具体需求,选择合适的创建和操作方式,以实现高效、健壮的程序。