【问题标题】:array of structs - size of array based on entries in file结构数组 - 基于文件中条目的数组大小
【发布时间】:2012-03-18 01:26:39
【问题描述】:

嗯,不确定这个标题是否有意义。 我正在创建一个从文件marks.txt 中读取信息的程序(顺便说一句,这是家庭作业,所以请耐心等待我还在黑暗中徘徊:))

marks.txt:

U08006 3 30 40 30
12000001 55 42 60
12000002 37 45 40
12000003 58 0 24
12000004 74 67 80
12000005 61 50 38
12000006 70 45 58
99999999

第一行是模块代码,后面是分配的数量和每个分配的权重。其他行是学生编号,后跟每项作业的分数。最后以学生号 99999999 表示列表的末尾。

编辑:程序最终应输出每个作业和整个模块标记的学生人数以及平均值和标准差。我还被指示使用结构数组。

我已经将模块信息存储在一个结构(模块)中,但是在存储学生信息时我真的迷路了。我需要为学生提供一个结构数组,但问题是我不知道文件中有多少学生(显然我现在知道,但是如果我希望我的程序能够读取文件,即使数字的学生更改具有固定大小的数组没有帮助)。

我在想也许我可以计算文件中第一行和最后一行之间的行数。然后使用 malloc 为数组分配内存?我在正确的轨道上吗?所以像 student = malloc(sizeof(student???) * 行数)

这真的让我很困惑!同时处理指针数组结构和文件真的让我一头雾水,这是我下一堂课的时间,所以不能向我的导师寻求帮助。因此,任何提示将不胜感激。

到目前为止,我的代码“正在进行中”: 编辑:好的,所以我已经完成了该程序,即它完成了它应该做的事情,但我仍在为整个 malloc 事情苦苦挣扎。对于我的学生结构,我想要一个结构数组,并且我想在确定学生人数后分配内存,但我不确定我应该如何做到这一点?!这个指向结构数组的整个指针我不太明白,所以下面的代码不起作用。 (如果我有一个预定大小的 struct student 数组,一切都很漂亮)

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

int studentCount();
void getModuleDetails(FILE *fileptr);
void getStudentDetails(FILE *fileptr, int classSize);
void overallMarks(int classSize);
void cleanUp(FILE *fileptr, int classSize);
void printModuleStats(int classSize);

struct ModuleSpec {
    char moduleCode[7];
    int numOfAssign;        /*number of assignments*/
    int *assignWeighting;   /*assignment weighting*/
} module;                   /*declare struct with variable name module*/

struct StudentMarks {
    int studentNumber;
    int *assignMark;        //assignment mark
    int moduleMark;         //overall mark for module
}*student;

/*******************************************************************************************/
int main() {
    int i; /*index*/
    FILE *pFile;

    int studentCounter = studentCount();    //count number of students in class

    student = malloc(sizeof(student)*studentCounter);   /*allocate memory for array of student structs*/

    pFile = fopen("marks.txt", "r");    /*open file marks.txt for reading*/
    if(pFile==NULL){    /*check if file was opened successfully*/
        printf("File could not be opened!");
        exit(EXIT_FAILURE);
    }

    getModuleDetails(pFile);   //get module details from pFile and store in struct module
    getStudentDetails(pFile, studentCounter);   //get student details from pFile and store in student array fo structs
    overallMarks(studentCounter);   //calculate and print overall marks of students
    printModuleStats(studentCounter);

    cleanUp(pFile, studentCounter);
    return 0;
}
/*****************************************************************************************/
int studentCount(){

    FILE *pFile;
    int temp;
    int studentCount = 0;

    pFile = fopen("marks.txt", "r");    /*open file marks.txt for reading*/
    if(pFile==NULL){    /*if file can't be opened*/
        printf("File could not be opened!");
        exit(EXIT_FAILURE);
    }

    do{
        temp = fgetc(pFile);
        if(temp == '\n'){
            studentCount++;
        }
    }while(temp != EOF);

    studentCount -=2;   /*subtract first and last lines to count only students*/

    //printf("The number of students on this module is: %d\n", studentCount);
    fclose(pFile);

    return studentCount;
}
/*******************************************************************************************/
void getModuleDetails(FILE *fileptr){
    int i;
    int sumWeighting = 0;

    fscanf(fileptr, "%s %d", module.moduleCode, &module.numOfAssign);

    printf("Module code: %s\n", module.moduleCode);
    printf("Number of assignments: %d\n", module.numOfAssign);

    module.assignWeighting = malloc(module.numOfAssign * sizeof(int)); /*Allocate memory to hold assignment weightings*/

    if (module.assignWeighting == NULL) {   /*check if memory allocation was successful*/
        printf("Memory allocation failed.");
        exit(EXIT_FAILURE);
    }

    /*get weighting for each assignment and store in module.assignWeighting*/
    for(i=0;i<module.numOfAssign;i++){ 
        fscanf(fileptr, "%d", &module.assignWeighting[i]);
        //printf("module %d %d\n", i+1, module.assignWeighting[i]);
        sumWeighting += module.assignWeighting[i];
    }

    /*check if sum of weighting equals 100*/
    if(sumWeighting != 100){
        printf("Error: Sum of weighting = %d\n Expected sum: 100\n", sumWeighting);
    }
}
/*********************************************************************************************/
void getStudentDetails(FILE *fileptr, int classSize){
    int i;
    int j;

    for(i=0;i<classSize;i++){
        student[i].assignMark = (int *)malloc(sizeof(int) * module.numOfAssign);
        if(student[i].assignMark == NULL) { //check if memory allocation was successful
            printf("Memory allocation failed.");
            exit(EXIT_FAILURE);
        }
        fscanf(fileptr, "%d", &student[i].studentNumber);

        /*get student assignment marks*/
        for(j=0;j<module.numOfAssign;j++){
            fscanf(fileptr, "%d", &student[i].assignMark[j]);
            //printf("mark for assignment %d: %d\n", j+1, student[i].assignMark[j] );
            /*check if mark is within range 0 to 100*/
            if(student[i].assignMark[j]<0 || student[i].assignMark[j]>100){
                printf("Error: Assignment mark is not within the range 0 to 100");
            }
        }
    }
}
/************************************************************************************************/
void overallMarks(int classSize){
    int i;
    int j;
    float temp;

    for(i=0;i<classSize;i++){
        printf("Overall mark for student %d: \n", student[i].studentNumber);

        for(j=0;j<module.numOfAssign;j++){
            temp += (float)(module.assignWeighting[j] * student[i].assignMark[j]) / 100; 
        }
        student[i].moduleMark = temp + 0.5;   /*add 0.5 for rounding as converting float to int rounds down*/
        printf("%d%%", student[i].moduleMark);

        if(student[i].moduleMark<25){
            printf(" is a FAIL\n");
        }
        else if(student[i].moduleMark<40){
            printf(" is a RESIT\n");
        }
        else if(student[i].moduleMark<55){
            printf(" is a PASS\n");
        }
        else if(student[i].moduleMark<70){
            printf(" is a MERIT\n");
        }
        else if(student[i].moduleMark<100){
            printf(" is a DISTINCTION\n");
        }

        temp = 0;
    }
}
/***********************************************************************************************/
void printModuleStats(int classSize){
    int i;
    int j;
    float averageDevMarks;
    float stdDevMarks;
    float averageDevModule;
    float stdDevModule;

    printf("\nModule Statistics for %s\n", module.moduleCode);
    printf("\nNumber of students: %d\n", classSize);
    /*assignments*/
    for(i=0;i<module.numOfAssign;i++){
        printf("\nAssignment %d:\n", i+1);
        for(j=0;j<classSize;j++){
        averageDevMarks += student[j].assignMark[i];
        }
        averageDevMarks /= classSize;
        printf("Average deviation: %f\n", averageDevMarks);

        for(j=0;j<classSize;j++){
        stdDevMarks += (student[j].assignMark[i] - averageDevMarks)*(student[j].assignMark[i] - averageDevMarks);
        }
        stdDevMarks = sqrt(stdDevMarks/classSize);
        printf("Standard deviation: %f\n", stdDevMarks);
        stdDevMarks = 0;
        averageDevMarks = 0;
    }
    /*modules*/
    for(i=0;i<classSize;i++){
        averageDevModule += student[i].moduleMark;
    }
    averageDevModule /= classSize;
    printf("\nAverage deviation for module mark: %f\n", averageDevModule);
    for(i=0;i<classSize;i++){
        stdDevModule += (student[i].moduleMark - averageDevModule)*(student[i].moduleMark - averageDevModule);
    }
    stdDevModule = sqrt(stdDevModule/classSize);
    printf("Standard deviation for module mark: %f\n", stdDevModule);   

}
/************************************************************************************************/
void cleanUp(FILE *fileptr, int classSize){
    int i;
    fclose(fileptr); /*close file*/

    /*free previously allocated memory*/
    free(student);
    free(module.assignWeighting);
    for(i=0;i<classSize;i++){
        free(student[i].assignMark);
    }
}

【问题讨论】:

  • 你需要对marks.txt中的数据做什么?一旦我们了解程序需要做什么,我们可能会更容易提供帮助。具体来说,您是否只需要为每个学生打印一个“总分”?如果是这种情况,您不需要存储比当前学生更多的数据。如果您需要每个作业的整体班级“平均值”,您可能仍然不需要每个学生的数据,只需要一些运行总计。
  • 您需要如何处理这些数据?看起来您要计算加权平均值。在这种情况下,您可以简单地一次读取一行,处理它,然后打印结果。这样,您就不必担心一次存储每个学生的成绩。
  • 抱歉,我一直盯着这个看太久了,我忘记了我为什么要这样做:)。我需要为每个作业和整体模块标记输出学生人数以及平均值和标准差。我还被指示使用结构数组
  • 如果只需要输出每个学生的分数或成绩,则不必将信息保存在内存中。如果您必须按成绩顺序(或分数顺序)输出学生,那么您确实需要将它们全部保存在内存中。请记住,您可以将动态内存分配与 malloc()realloc() 一起使用,或者您可以简单地确定最大班级人数是某个人数(30、300、1000)学生并预先分配该最大人数,然后抱怨该人数是否超过(通过阅读文件的其余部分来说明最大值应该有多大是礼貌的)。
  • @Astabh - 我是新来的,但我认为可以编辑您的问题并添加更多信息。我更喜欢那个。将所有问题放在一个地方似乎更好,而不是必须阅读 cmets 才能发现整个问题。当他们更清楚所要求的内容时,它可能会鼓励其他人提供帮助。

标签: c


【解决方案1】:

您可以使用链表而不是 Luchian Grigore 建议的数组,也可以使用动态分配的结构数组,或者您可以使用动态分配的指针数组来动态分配结构。后者的优点是分配数组时需要做的复制较少;缺点是要释放的内存分配较多。

这如何转化为代码?这是“动态分配的指向动态分配结构的指针数组”的概述。我假设一个例程来阅读单个学生的信息;它也会分配一个具有正确数量标记的结构。基本上,它从文件中读取一行(发现 EOF 并在读取标记值时返回一个空指针)。对于学生,它分配一个学生标记结构和一个大小合适的整数数组。它解析学生编号和每个年级的行。如果缺少标记,它可以报告错误。它返回指向已分配学生的指针。我假设有两个单独的分配——一个用于学生标记结构,另一个用于标记数组。

typedef struct StudentMarks Marks;

static Marks *read_student_marks(FILE *fp, int num_grades);

Marks  **marks     = 0;  /* Array of pointers to student marks */
size_t   num_marks = 0;  /* Number of marks in use */
size_t   max_marks = 0;  /* Number of marks allocated */
Marks   *student;

while ((student = read_student_marks(fp, num_grades)) != 0)
{
    assert(num_marks <= max_marks);
    if (num_marks == max_marks)
    {
        /* Not enough space left - allocate more */
        size_t new_size = max_marks * 2 + 2;
        Marks **new_marks = realloc(marks, new_size * sizeof(*marks));
        if (new_marks == 0)
            ...handle out of memory error...
            ...NB: you still have the original array to work with...
        marks = new_marks;
        max_marks = new_size;
    }
    marks[num_marks++] = student;
}

这从一个小分配(2 个条目)开始,以便执行重新分配代码。我还利用了这样一个事实,即如果您将 NULL 指针传递给realloc(),它将分配新内存。另一个版本将使用malloc() 进行初始分配,然后使用realloc()

size_t   num_marks = 0;
size_t   max_marks = 2;
Marks  **marks = malloc(max_marks * sizeof(*marks));

if (marks == 0)
    ...handle out of memory condition...

while ((students = ...

如果您担心在循环结束时分配了太多空间,您可以通过以下方式释放剩余空间:

marks = realloc(marks, num_marks * sizeof(*marks));
max_marks = num_marks;

发布代码为:

for (i = 0; i < num_marks; i++)
{
    free(marks[i]->assignMark);
    free(marks[i]);
}
free(marks);
marks = 0;
num_marks = 0;
max_marks = 0;

如果您想分配学生分数数组,您需要决定学生阅读器功能的工作方式。您可以使用上述设计;您将返回的结构复制到分配的数组中。然后,您只释放标记结构(但不释放标记数组;仍在使用中)。您可以使用与我在上面所做的非常相似的方式来扩展数组。不过,发布代码不同(更简单):

for (i = 0; i < num_marks; i++)
    free(marks[i]->assignMarks);
free(marks);
marks = 0;
num_marks = 0;
max_marks = 0;

ASCII 艺术来拯救——也许……

第一个代码假设您从read_student_marks() 函数中获得指向学生的一组分数的指针。它保留了一个指向这些条目的指针数组:

+----------+         +--------------------+
| marks[0] |-------->| marks for 12000001 |
+----------+         +--------------------+      +--------------------+
| marks[1] |------------------------------------>| marks for 12000002 |
+----------+         +--------------------+      +--------------------+
| marks[2] |-------->| marks for 12000003 |
+----------+         +--------------------+      +--------------------+
| marks[3] |------------------------------------>| marks for 12000004 |
+----------+                                     +--------------------+

注意marks 数组是如何连续的,但每个学生的分数是单独分配的。代码会在需要增长时定期重新分配 marks 数组,但不会移动每个学生的个人标记。

概述的替代方案如下:

+--------------------+
| marks for 12000001 |
+--------------------+
| marks for 12000002 |
+--------------------+
| marks for 12000003 |
+--------------------+
| marks for 12000004 |
+--------------------+

在这里,您将重新分配所有内存,复制完整的标记结构。在某些层面上,这比我最初概述的方案要简单。

使这个讨论复杂化的是,“标记”结构并不像我展示的那样简单:

+--------------------------+
| studentNumber:  12000001 |            +------+------+------+
| assignMark:     *--------=----------->|  52  |  45  |  60  |
| moduleMark:           92 |            +------+------+------+
+--------------------------+

如果你有一个 C99 编译器,你可以为结构的最后一个元素使用带有“灵活数组成员”的结构;这将保持标记:

struct StudentMarks
{
    int studentNumber;
    int moduleMark;
    int assignMark[];       // Flexible array member
}

您可以使用如下符号来分配它:

struct StudentMarks *marks = malloc(sizeof(*marks) +
                                    num_assignments * sizeof(marks->assignMark[0]));

这会在单个内存分配中分配学生标记结构和数组。但是,您不能拥有具有灵活数组成员的结构数组;您只能使用指向具有灵活数组成员的结构的指针数组(让您回到我展示的第一个代码)。

如果您还没有收集,有不止一种方法可以做到这一点 - 这是 Perl 的座右铭(又名 TMTOWTDI),但在这里很适用。

作为一些建议,我建议绘制与我所做的类似的指针图,以帮助您了解自己在做什么。一段时间后,它们将不再是必要的,但图表可以在您需要它们时提供很大的帮助。

【讨论】:

  • 感谢您的帮助。我必须承认我不太明白你的代码第一部分发生了什么。就像我提到的,我对指针和 malloc 等非常陌生。
  • +1 看到这样的巨大努力被忽视,我很难过。
  • 非常感谢您,我没有注意到您已经编辑了您的帖子!我现在正在看:)
【解决方案2】:

预读学生人数需要两次通过文件。

更简洁的解决方案是实现linked list,并在每次阅读新学生时添加。

【讨论】:

    【解决方案3】:

    考虑到如何编写程序的一些限制,这里概述了一种方法:

    读取模块信息,并将值放入具有module_name和number_of_assignments的module_struct中(这不需要malloc'd)

    malloc 为数组分配足够的空间来保存每个 assignment_weighting,然后将权重放入其中。

    扫描文件,计算学生人数。

    malloc 足够的空间用于一个 student_structs 数组,number_of_students 长

    对于 student_structs 数组中的每个条目,分配足够的空间来保存 assignment_marks,并将 student_struct 条目中的一个字段指向该 assignment_marks 数组

    现在应该有足够的空间来保存文件中的所有值

    使用 fseek 或 fclose 并再次打开文件将文件倒回到开头。

    扫过模块头

    读取每个学生行,将值分配到 student_structs 及其 assignment_marks 的条目中

    对 student_structs 数组进行所有计算,并打印答案。

    这些信息足以让你感动吗?

    【讨论】:

    • 谢谢,这确实让它更清楚了。但我对计数学生部分有严重的问题。它可以独立运行,但是当我尝试在我的 for 循环中使用计数时,我突然得到了 53 个学生?!我看不出这是哪里出了问题!我已经用更新的代码编辑了我的帖子。
    • 您希望文件中有多少学生?我注意到代码正在计算“\n”,但没有检查“99999999”标记。如果文件末尾有一些空行,或者最后一行没有'\n',则计数错误。
    • 6 名学生。计数器自己工作(我刚刚为第一行和最后一行减去了 2)!但由于某种原因,当我在底部添加代码时,我的学生计数器突然变为 53。
    • 哦,真令人沮丧。有些东西可能会破坏它的价值。你有一个可以观察变量的调试器吗?这会让你运行,当变量值改变时它会停止。
    • hmmm.. 嗯,我正在使用 gcc,不幸的是,对调试一无所知,在课程中还没有完全做到。很确定我有几个版本的视觉工作室漂浮在周围。我会看看谷歌想出什么
    猜你喜欢
    • 1970-01-01
    • 2015-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-12
    • 1970-01-01
    • 2021-12-19
    相关资源
    最近更新 更多