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

C语言结构体数组的遍历与处理

2023-10-106.7k 阅读

C语言结构体数组的遍历与处理

结构体数组的基本概念

在C语言中,结构体是一种用户自定义的数据类型,它允许将不同类型的数据组合在一起。当我们需要处理多个具有相同结构体类型的对象时,就可以使用结构体数组。结构体数组中的每个元素都是一个结构体变量。

例如,假设我们要描述一个学生的信息,包括姓名、年龄和成绩,我们可以定义如下结构体:

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

然后我们可以定义一个结构体数组来存储多个学生的信息:

struct Student students[3];

这里students就是一个包含3个struct Student类型元素的结构体数组。

结构体数组的初始化

  1. 逐个初始化 我们可以像普通数组一样,对结构体数组的每个元素进行逐个初始化。

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

    在这个例子中,我们分别为数组中的三个元素进行了初始化,每个花括号内的值对应结构体中成员的顺序。

  2. 部分初始化 也可以只初始化数组的部分元素,未初始化的元素会被自动初始化为0(对于数值类型)或空字符(对于字符数组)。

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

    这里students[0]被初始化,students[1]students[2]name成员会被初始化为空字符串,age成员被初始化为0,score成员被初始化为0.0。

结构体数组的遍历

  1. 使用for循环遍历 最常见的遍历结构体数组的方式是使用for循环。通过循环索引,我们可以访问数组中的每个结构体元素,并进一步访问其成员。

    #include <stdio.h>
    
    struct Student {
        char name[20];
        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("Student %d:\n", i + 1);
            printf("Name: %s\n", students[i].name);
            printf("Age: %d\n", students[i].age);
            printf("Score: %.2f\n\n", students[i].score);
        }
    
        return 0;
    }
    

    在上述代码中,for循环从0到2(因为数组下标从0开始,且数组长度为3)。在每次循环中,通过students[i]访问结构体数组的第i个元素,然后使用点运算符(.)访问该元素的各个成员并输出。

  2. 使用指针遍历 我们也可以使用指针来遍历结构体数组。由于数组名本身就是一个指向数组首元素的指针,我们可以利用这一点进行遍历。

    #include <stdio.h>
    
    struct Student {
        char name[20];
        int age;
        float score;
    };
    
    int main() {
        struct Student students[3] = {
            {"Alice", 20, 85.5},
            {"Bob", 21, 90.0},
            {"Charlie", 20, 78.0}
        };
    
        struct Student *ptr = students;
    
        for (int i = 0; i < 3; i++) {
            printf("Student %d:\n", i + 1);
            printf("Name: %s\n", ptr->name);
            printf("Age: %d\n", ptr->age);
            printf("Score: %.2f\n\n", ptr->score);
            ptr++;
        }
    
        return 0;
    }
    

    这里我们定义了一个struct Student类型的指针ptr,并将其初始化为students(即数组首地址)。在循环中,通过ptr->来访问结构体成员,每次循环后ptr自增,指向下一个结构体元素。使用指针遍历在某些情况下可以提高代码的执行效率,尤其是在处理大型结构体数组时。

结构体数组的常见处理操作

  1. 查找特定元素 假设我们要在学生结构体数组中查找名为“Bob”的学生信息。

    #include <stdio.h>
    #include <string.h>
    
    struct Student {
        char name[20];
        int age;
        float score;
    };
    
    int main() {
        struct Student students[3] = {
            {"Alice", 20, 85.5},
            {"Bob", 21, 90.0},
            {"Charlie", 20, 78.0}
        };
    
        char targetName[] = "Bob";
        int found = 0;
    
        for (int i = 0; i < 3; i++) {
            if (strcmp(students[i].name, targetName) == 0) {
                printf("Student found:\n");
                printf("Name: %s\n", students[i].name);
                printf("Age: %d\n", students[i].age);
                printf("Score: %.2f\n", students[i].score);
                found = 1;
                break;
            }
        }
    
        if (!found) {
            printf("Student not found.\n");
        }
    
        return 0;
    }
    

    在这段代码中,我们使用strcmp函数来比较字符串。如果找到匹配的学生,就输出其信息并设置found标志为1,然后跳出循环。如果循环结束后found仍为0,则表示未找到该学生。

  2. 排序 对结构体数组进行排序可以帮助我们更方便地查找和处理数据。例如,我们可以按照学生的成绩对结构体数组进行升序排序。这里我们使用冒泡排序算法来演示。

    #include <stdio.h>
    
    struct Student {
        char name[20];
        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("Student %d:\n", i + 1);
            printf("Name: %s\n", students[i].name);
            printf("Age: %d\n", students[i].age);
            printf("Score: %.2f\n\n", students[i].score);
        }
    
        return 0;
    }
    

    在上述代码中,我们定义了一个swap函数来交换两个结构体元素。sortByScore函数使用冒泡排序算法对结构体数组按成绩进行排序。排序后,我们再次遍历数组并输出学生信息,此时学生信息将按成绩升序排列。

  3. 修改元素值 有时我们需要修改结构体数组中某个元素的成员值。例如,我们要将名为“Alice”的学生的成绩提高5分。

    #include <stdio.h>
    #include <string.h>
    
    struct Student {
        char name[20];
        int age;
        float score;
    };
    
    int main() {
        struct Student students[3] = {
            {"Alice", 20, 85.5},
            {"Bob", 21, 90.0},
            {"Charlie", 20, 78.0}
        };
    
        char targetName[] = "Alice";
    
        for (int i = 0; i < 3; i++) {
            if (strcmp(students[i].name, targetName) == 0) {
                students[i].score += 5;
                printf("Student's score updated:\n");
                printf("Name: %s\n", students[i].name);
                printf("New Score: %.2f\n", students[i].score);
                break;
            }
        }
    
        return 0;
    }
    

    这段代码通过遍历结构体数组,找到名为“Alice”的学生,然后将其成绩增加5分,并输出更新后的成绩。

结构体数组与函数

  1. 传递结构体数组到函数 我们可以将结构体数组作为参数传递给函数,以便在函数中对其进行处理。例如,我们定义一个函数来计算学生的平均成绩。

    #include <stdio.h>
    
    struct Student {
        char name[20];
        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("Average score: %.2f\n", average);
    
        return 0;
    }
    

    在这个例子中,calculateAverage函数接受一个结构体数组和数组的长度作为参数。函数内部通过遍历数组计算总成绩,然后返回平均成绩。

  2. 从函数返回结构体数组 虽然不常见,但也可以从函数返回结构体数组。不过需要注意的是,不能直接返回局部数组,因为局部数组在函数结束时会被销毁。通常可以通过返回指向静态数组或动态分配内存的指针来解决这个问题。以下是返回静态数组的示例:

    #include <stdio.h>
    
    struct Student {
        char name[20];
        int age;
        float score;
    };
    
    struct Student* createStudents() {
        static struct Student students[3] = {
            {"Alice", 20, 85.5},
            {"Bob", 21, 90.0},
            {"Charlie", 20, 78.0}
        };
        return students;
    }
    
    int main() {
        struct Student *students = createStudents();
        for (int i = 0; i < 3; i++) {
            printf("Student %d:\n", i + 1);
            printf("Name: %s\n", students[i].name);
            printf("Age: %d\n", students[i].age);
            printf("Score: %.2f\n\n", students[i].score);
        }
    
        return 0;
    }
    

    createStudents函数中,我们定义了一个静态的结构体数组students,并返回其指针。因为静态数组的生命周期与程序相同,所以在函数外部可以安全地访问它。

结构体数组的内存管理

  1. 栈上的结构体数组 当我们在函数内部定义结构体数组时,它存储在栈上。例如:

    void someFunction() {
        struct Student students[3];
        // 对students进行操作
    }
    

    栈上的数组在函数结束时会自动释放内存,无需手动管理。但栈的空间有限,如果结构体数组过大,可能会导致栈溢出。

  2. 堆上的结构体数组 为了避免栈溢出问题,我们可以在堆上动态分配结构体数组的内存。这需要使用malloccalloc函数。

    #include <stdio.h>
    #include <stdlib.h>
    
    struct Student {
        char name[20];
        int age;
        float score;
    };
    
    int main() {
        int n = 3;
        struct Student *students = (struct Student *)malloc(n * sizeof(struct Student));
        if (students == NULL) {
            printf("Memory allocation failed.\n");
            return 1;
        }
    
        // 初始化数组
        for (int i = 0; i < n; i++) {
            sprintf(students[i].name, "Student%d", i + 1);
            students[i].age = 20 + i;
            students[i].score = 80.0 + i * 2.0;
        }
    
        // 遍历数组
        for (int i = 0; i < n; i++) {
            printf("Student %d:\n", i + 1);
            printf("Name: %s\n", students[i].name);
            printf("Age: %d\n", students[i].age);
            printf("Score: %.2f\n\n", students[i].score);
        }
    
        free(students);
        return 0;
    }
    

    在上述代码中,我们使用malloc函数在堆上分配了足够存储3个struct Student类型元素的内存。在使用完数组后,必须使用free函数释放内存,以避免内存泄漏。

结构体数组与文件操作

  1. 将结构体数组写入文件 我们可以将结构体数组的内容写入文件,以便持久化存储数据。例如,将学生信息写入一个文本文件。

    #include <stdio.h>
    
    struct Student {
        char name[20];
        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("Failed to open file.\n");
            return 1;
        }
    
        for (int i = 0; i < 3; i++) {
            fprintf(file, "Name: %s, Age: %d, Score: %.2f\n", students[i].name, students[i].age, students[i].score);
        }
    
        fclose(file);
        return 0;
    }
    

    这段代码使用fopen函数以写入模式打开一个文件。然后通过fprintf函数将每个学生的信息格式化写入文件,最后使用fclose函数关闭文件。

  2. 从文件读取结构体数组 要从文件中读取数据并填充结构体数组,我们需要先打开文件,然后按行读取并解析数据。

    #include <stdio.h>
    #include <stdlib.h>
    
    struct Student {
        char name[20];
        int age;
        float score;
    };
    
    int main() {
        struct Student students[3];
        FILE *file = fopen("students.txt", "r");
        if (file == NULL) {
            printf("Failed to open file.\n");
            return 1;
        }
    
        for (int i = 0; i < 3; i++) {
            if (fscanf(file, "Name: %19s, Age: %d, Score: %f", students[i].name, &students[i].age, &students[i].score) != 3) {
                printf("Error reading file.\n");
                fclose(file);
                return 1;
            }
        }
    
        fclose(file);
    
        // 遍历数组
        for (int i = 0; i < 3; i++) {
            printf("Student %d:\n", i + 1);
            printf("Name: %s\n", students[i].name);
            printf("Age: %d\n", students[i].age);
            printf("Score: %.2f\n\n", students[i].score);
        }
    
        return 0;
    }
    

    这里我们使用fscanf函数从文件中按指定格式读取数据并填充到结构体数组中。注意在读取字符串时,要限制读取的字符数,以避免缓冲区溢出。

结构体数组的嵌套与复杂应用

  1. 结构体数组嵌套结构体 结构体数组可以包含嵌套的结构体。例如,假设每个学生除了基本信息外,还有一个家庭住址结构体。

    #include <stdio.h>
    
    struct Address {
        char street[30];
        char city[20];
        int zip;
    };
    
    struct Student {
        char name[20];
        int age;
        float score;
        struct Address address;
    };
    
    int main() {
        struct Student students[2] = {
            {"Alice", 20, 85.5, {"123 Main St", "Anytown", 12345}},
            {"Bob", 21, 90.0, {"456 Elm St", "Othercity", 67890}}
        };
    
        for (int i = 0; i < 2; i++) {
            printf("Student %d:\n", i + 1);
            printf("Name: %s\n", students[i].name);
            printf("Age: %d\n", students[i].age);
            printf("Score: %.2f\n", students[i].score);
            printf("Address: %s, %s, %d\n\n", students[i].address.street, students[i].address.city, students[i].address.zip);
        }
    
        return 0;
    }
    

    在这个例子中,struct Student结构体包含一个struct Address类型的成员。初始化和访问嵌套结构体成员时,需要使用多层点运算符。

  2. 动态分配嵌套结构体数组 当处理复杂的嵌套结构体数组时,动态内存分配变得尤为重要。例如,我们可能需要根据用户输入动态创建包含嵌套结构体的学生数组。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct Address {
        char street[30];
        char city[20];
        int zip;
    };
    
    struct Student {
        char name[20];
        int age;
        float score;
        struct Address address;
    };
    
    int main() {
        int n;
        printf("Enter the number of students: ");
        scanf("%d", &n);
    
        struct Student *students = (struct Student *)malloc(n * sizeof(struct Student));
        if (students == NULL) {
            printf("Memory allocation failed.\n");
            return 1;
        }
    
        for (int i = 0; i < n; i++) {
            printf("Enter details for student %d:\n", i + 1);
            printf("Name: ");
            scanf("%19s", students[i].name);
            printf("Age: ");
            scanf("%d", &students[i].age);
            printf("Score: ");
            scanf("%f", &students[i].score);
            printf("Street: ");
            scanf("%29s", students[i].address.street);
            printf("City: ");
            scanf("%19s", students[i].address.city);
            printf("Zip: ");
            scanf("%d", &students[i].address.zip);
        }
    
        for (int i = 0; i < n; i++) {
            printf("Student %d:\n", i + 1);
            printf("Name: %s\n", students[i].name);
            printf("Age: %d\n", students[i].age);
            printf("Score: %.2f\n", students[i].score);
            printf("Address: %s, %s, %d\n\n", students[i].address.street, students[i].address.city, students[i].address.zip);
        }
    
        free(students);
        return 0;
    }
    

    在这个代码中,我们首先根据用户输入的学生数量动态分配内存。然后通过用户输入填充每个学生及其嵌套的地址结构体的信息。最后,遍历并输出这些信息,记得释放动态分配的内存。

通过以上对C语言结构体数组的遍历与处理的详细介绍,你应该对如何使用结构体数组有了更深入的理解,无论是简单的初始化、遍历,还是复杂的操作和内存管理,都可以根据实际需求灵活运用。在实际编程中,合理使用结构体数组可以有效地组织和处理复杂的数据,提高程序的可读性和可维护性。