结构数组 - 基于文件中的条目的数组大小

时间:2012-03-18 01:26:40

标签: c

嗯,不确定这个标题是否有意义。 我正在创建一个从文件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(学生???)*行数)

这真让我困惑!一下子处理指针数组结构和文件实际上让我感到困惑,这是因为我下一节课时所以不能向我的导师寻求帮助。所以任何提示都会非常感激。

到目前为止,我的代码完全是“正在进行的工作”: 编辑:好的所以我已经完成了程序,即它完成了它的设想,但我仍在努力完成整个malloc的事情。对于我的学生结构我想要一个结构数组,我想在确定学生数量后分配内存,但我不确定我是怎么想这样做的?!这整个指向结构数组的指针我还没有得到它所以下面的代码不起作用。 (如果我有一个预定大小的结构学生数组,一切都很好)

#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);
    }
}

3 个答案:

答案 0 :(得分:3)

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

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

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 |
+--------------------+

在这里,您将重新分配所有内存,复制完整的标记结构。在某些级别,这比我首先概述的方案更简单。

使这种讨论复杂化的是,''for'结构并不像我所展示的那样简单:

+--------------------------+
| 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)的座右铭,但在这里很适用。

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

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

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

malloc足够的空间让一个数组保存每个assignment_weighting,然后将权重加入其中。

扫描文件,计算number_of_students。

malloc足够的空间用于student_structs数组,number_of_students long

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

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

将文件回放到开头,使用fseek,或者fclose并再次使用fcln。

扫描模块标题

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

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

这足以让你感动吗?