gpt4 book ai didi

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

转载 作者:太空宇宙 更新时间:2023-11-04 06:40:08 27 4
gpt4 key购买 nike

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

标记.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???) * number of lines)

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

到目前为止,我的代码都是“正在进行中的工作”:
编辑:好的,所以我已经完成了这个程序,即它做了它应该做的事情,但我仍然在为整个 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);
}
}

最佳答案

您可以使用链表而不是 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),但在这里很适用。

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

关于c - 结构数组 - 基于文件中条目的数组大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9755266/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com