【问题标题】:Need help changing Structure of Array to Array of Structures需要帮助将结构数组更改为结构数组
【发布时间】:2016-07-03 14:39:39
【问题描述】:

我有以下工作代码。但是,我误读了指令并制作了一个包含数组的结构,而不是结构数组。

下面是来自数组结构的工作代码。下面是我认为我对将其更改为结构数组的理解,这可能是错误的。我还需要弄清楚在最后打印时如何对输入的学生 ID 进行排序,但这并不重要。感谢您的帮助!

#include <stdio.h>

struct student {
    int studID;
    int marks[100];
    };

int main() {

    struct student s[100];
    int i, j, val, flag=0;

    printf(" \n Please enter the students ID# followed by their grades. When done entering\n");
    printf(" grades, enter 101 to start a new student or 102 to exit entry.\n\n\n ");

    for(i=0;i<10;) {

        printf("Enter student ID#: ");
        scanf("%d",&s[i].studID);

        printf("\nFor student ID# %d\n",s[i].studID);

        for(j=0;j<100;j++) {
            printf("Enter grades: ");
            scanf("%d",&val);

            if(val>=0 && val<=100) {
                s[i].marks[j]=val;
                }
            else if(val==101) {
                s[i].marks[j]=-1;
                break;
                }
            else if(val==102) {
                s[i].marks[j]=-1;
                flag=1;
                break;
                }
            else {
                printf("\ninvalid entry\n");
                j--;
                }
            }

        printf("\n\n----------\n\n");

        i++;

        if(flag==1)
            break;
        }

    int num=i;

    for(i=0; i<num ; i++) {
        printf("\nInformation for student ID number %d:\n",s[i].studID);
        for(j=0; s[i].marks[j]!=-1; j++)
        printf("Grades: %d\n",s[i].marks[j]);
        }

    return 0;
 }

我认为我需要做的是

struct student {
    int studID;
    int marks;  //remove the array from marks
    };

int main() {

    struct student s[100];
    struct student m[100];  //maybe add this second array?

我不确定在那之后该去哪里。如果我从结构中的标记中删除数组,s[i].marks[j] 的任何位置都会出现错误“下标值既不是数组也不是指针也不是向量”。任何有关如何解决这个问题的帮助将不胜感激。

【问题讨论】:

  • 不,您所做的正是您开始时需要做的。您将有多个学生,每个学生都有多个成绩。这意味着您将拥有包含标记数组的任何结构数组——就像您拥有的一样。您唯一明显的遗漏是每个学生的成绩计数(例如,在struct student 中添加unsigned nmarks;,这样您就知道每个学生有多少成绩。您唯一的决定是您是否使用自动存储(这限制了您的初始分配)或根据需要使用malloc(或更好的calloc)和realloc动态分配。我会简单地添加nmarks

标签: c arrays sorting struct


【解决方案1】:

误读指令并创建了一个包含数组的结构,而不是结构数组

简单的答案是

不,结构数组可以由具有数组作为其成员的结构或数组结构组成

在您的代码中为以下结构:

struct student  //this is a structure of array
{
    int studID;
    int marks[100];
};

struct student s[100] 绝对是一个结构数组。所以你没有按照说明做任何事情,你的第一个代码确实是结构数组的实现:)

但是,如果不允许使用数组作为结构成员,请阅读其余答案。


不使用数组结构实现:

问题: (用你的方法)

struct student
{
   int studID;
   int marks; //you made array to a single variable
};
  • 好吧,您已经删除了结构内部的数组,但是,正如您所提到的:现在您无法访问 s[i].marks[j] 因为现在 marks 是一个保存单个整数的变量,它不能通过使用像s[i].marks[index] 这样的索引来存储多个值

解决方案:

  • 如果您的目标只是避免使用数组作为结构成员,并且如果您想访问marks 并使用下标存储它,那么您可以将marks 声明为指针使用 malloc() 函数 (include stdlib.h file) 到动态分配内存的基地址,如下所示:

    struct student
    {
       int studID;
       int *marks;
    };
    //so now in this structure there is no array, 
    //you just have a integer and integer pointer as members 
    
  • 然后,在第一个 for 循环中,添加:

    for(i=0;i<10;) 
    {
    
        printf("Enter student ID#: ");
        scanf("%d",&s[i].studID);
    
        s[i].marks = malloc(100*sizeof(int)); //here you are allocating memory
    
  • 您正在为要输入的100 等级分配内存。现在,如果用户只是为学生输入10 成绩,那么为90 更多成绩分配的剩余内存将被浪费。您可以通过将s[i].marks 的大小重新分配给足够数量的等级来避免这种情况

    • 当用户输入101时,它标志着当前student的成绩/分数结束,所以你可以这样重新分配:

      else if(val==101)
      {
          s[i].marks[j]=-1;
          s[i].marks = realloc(s[i].marks,(j+1)*sizeof(int)); //resize
          break;
      }  
      //`(j+1)*sizeof(int)` because there are `j` number of grades
      //and an additional `-1` to mark the end of array.
      
    • 同样,当您输入102 时,您可以这样调整s[i].marks 的大小:

       else if(val==102)
      {
          s[i].marks[j]=-1;
          s[i].marks = realloc(s[i].marks,(j+1)*sizeof(int)); //resize
          flag=1;
          break;
       }
      
  • 现在您可以使用s[i].marks[j]访问元素

  • 最后不要忘记以这种方式使用free() 函数释放分配的内存:

    for(i=0; i<num ; i++)
    {
        free(s[i].marks);
    }
    

  • 您的代码总共是:

(不使用数组结构

#include <stdio.h>
#include <stdlib.h> //don't forget to include this

struct student
{
    int studID;
    int *marks;
};

int main(void) 
{

    struct student s[100];
    int i, j, val, flag=0;

    printf(" \n Please enter the students ID# followed by their grades. When done entering\n");
    printf(" grades, enter 101 to start a new student or 102 to exit entry.\n\n\n ");

    for(i=0;i<10;) 
    {

        printf("Enter student ID#: ");
        scanf("%d",&s[i].studID);

        s[i].marks = malloc(100*sizeof(int)); //allocating memory

        printf("\nFor student ID# %d\n",s[i].studID);

        for(j=0;j<100;j++) 
        {
            printf("Enter grades: ");
            scanf("%d",&val);

            if(val>=0 && val<=100) 
            {
                s[i].marks[j]=val;
            }
            else if(val==101) 
            {
                s[i].marks[j]=-1;
                s[i].marks = realloc(s[i].marks,(j+1)*sizeof(int)); //resize
                break;
            }
            else if(val==102) 
            {
                s[i].marks[j]=-1;
                s[i].marks = realloc(s[i].marks,(j+1)*sizeof(int)); //resize
                flag=1;
                break;
            }
            else 
            {
                printf("\ninvalid entry\n");
                j--;
            }
        }

        printf("\n\n----------\n\n");

        i++;

        if(flag==1)
            break;
    }

    int num=i;

    for(i=0; i<num ; i++) 
    {
        printf("\nInformation for student ID number %d:\n",s[i].studID);
        for(j=0; s[i].marks[j]!=-1; j++)
        printf("Grades: %d\n",s[i].marks[j]);
    }


    //freeing memory    
    for(i=0; i<num ; i++)
    {
        free(s[i].marks);
    }

 }

【讨论】:

  • @PhilipHudson 你不能使用stdlin.h 文件吗?
  • 如果是动态分配marks,为什么不分配s,然后根据需要检查每个和realloc上的索引?
  • 你的意思是,不是为每个 `s[i].marks' 分配100*sizeof(int),我是否应该为用户为每个 s[i] @ 输入的数字等级分配足够的内存大卫 C.兰金
  • 是的,一般的方法是为预期的学生数量(比如每班 32 名)和预期的成绩数量(比如每门课程 12 名)分配足够的内存。然后,如果你达到 32 名学生,你 realloc 的数量 stuct student 一定数量(比如 16 个左右),如果你达到给定学生的分数限制,你 realloc 任何有意义的分数( 3个额外的信用等)如果你首先动态分配malloc(或calloc)等,你只能realloc
  • 这就是我要走的路。分配一些合理预期数字的一般规则(为了避免不断调用realloc,不了解更多关于OP 的数据,100 很好)。 realloc 根据需要添加一些恒定数量的int(比如另一个100)或当前总内存的一些倍数(例如2x)。该方法适用于 OP 代码中的 smarks。我已经摆脱了将当前内存池的 2x 加倍,而是支持添加一些固定数量(例如,此处为另一个 100)以防止内存使用增长过快。
【解决方案2】:

//maybe add this second array?

我不这么认为。你说你想要一个结构数组。这就是s。结构包含一个数组这一事实对我来说似乎不是问题,除非它被某些奇怪的规则明确禁止。

我不确定在那之后该去哪里。如果我删除数组 从结构中的标记,任何地方s[i].marks[j] 我得到错误 “下标值既不是数组也不是指针也不是向量”。

你当然知道。如果删除数组维度,则marksint,而不是数组。只有 s 是一个数组。只有数组或指针可以是[indexed]

但是看看你原来的例子。你有 10 个学生,每个学生有 100 分。通过将数组保留在您的 struct 定义中,代码应该可以正常工作。你说'获取学生i 然后他们的标记j'。这应该可以正常工作。不是吗?你得到什么错误?

【讨论】:

  • 说明书中没有说你不能,但是我从工作代码收到的反馈表明他有问题。
  • 那是什么问题?你的导师真的要求你制作一个包含分数的第二个数组,与相应的学生分开吗?以后你将如何将学生与他们的分数重新联系起来?无论如何,SO 不是一个代码编写服务,所以我们需要一个明确的问题来处理。从所有“我认为”和“我不太确定”来看,我认为现在没有。如果可能,请向您的讲师索取更多信息,并提供指导。让我们猜测他们想要什么可能会使事情变得更糟:我们要么弄错,要么弄对,但你可能不明白
【解决方案3】:

从评论开始,您的包含数组的结构数组一开始是正确的。如前所述,唯一缺少的部分是添加一个成员来获取/存储每个 studentID 的分数(成绩)。添加这将允许逻辑工作并简化处理程序输出。实际上,它会简化为类似于以下的逻辑(包括按 ID 对结果进行排序):

...
/* constants for max students and marks */
enum { NSTD = 64, NMARK = 100 };

typedef struct {
    int sid;            /* student ID    */
    int nmarks;         /* number grades */
    int marks[NMARK];   /* grades array  */
} student;

int main (int argc, char **argv) {

    int i, j, sidx = 0;
    student s[NSTD] = {{ .sid = 0 }};
    ...
    for (; sidx < NSTD;) {  /* for each id up to max NSTD */
        int grade = 0;

        printf (" enter student ID: ");     /* enter/validate ID */
        while (scanf (" %d", &s[sidx].sid) != 1) { ... }

        printf ("  Enter grades for student %d: ", s[sidx].sid);
        while (scanf (" %d", &grade) == 1) {    /* for each grade */
            ...
            s[sidx].marks[s[sidx].nmarks++] = grade;
        }
        sidx++;     /* increment student index */
    }
    ...

输入所有数据并知道每个 studentID 有多少成绩后,对数据的迭代就变成了对每个 studentID 和标记的简单嵌套循环,例如:

for (i = 0; i < sidx; i++) {
    printf ("\n Student ID : %3d     grades :", s[i].sid);
    for (j = 0; j < s[i].nmarks; j++)
        printf ("  %2d", s[i].marks[j]);
}

要对数据进行排序(或者当您认为排序 C 中的任何内容时)使用qsortqsort 的唯一挑战是编写一个小函数来告诉 qsort 如何对您传递的任何内容进行排序。 (您将传递一个指向student 的指针,因此只需根据student-&gt;sid 进行排序)。由于比较函数的输入将是 const void *(一个常量 void 指针),因此您只需将其转换为 student * 并引用 ID 成员:

int cmpsid (const void *a, const void *b)
{
    student *s1 = (student *)a;  /* technically you should add 'const' */
    student *s2 = (student *)b;  /* omitted for simmplicity */

    /* comparing (a > b) - (a < b) prevents potential overflow */
    return (s1->sid > s2->sid) - (s1->sid < s2->sid);
}

您可以通过在比较中简单地应用转换来避免中间变量 s1s2

int cmpsid (const void *a, const void *b)
{
    /* (a > b) - (a < b) prevents potential overflow */
    return (((student *)a)->sid > ((student *)b)->sid) - 
            (((student *)a)->sid < ((student *)b)->sid);
}

(以你更清楚的为准)

您调用qsort 对结构s 的数组进行排序很简单:

qsort (s, sidx, sizeof *s, cmpsid); /* sort by student ID */

将其放在一起并添加有意义的其他验证,您可以执行以下操作。从文本文件中读取数据的代码(如果没有给出文件名,则默认提示输入):

#include <stdio.h>
#include <stdlib.h>     /* for qsort */

/* constants for max students and marks */
enum { NSTD = 64, NMARK = 100 };

typedef struct {
    int sid;            /* student ID    */
    int nmarks;         /* number grades */
    int marks[NMARK];   /* grades array  */
} student;

int cmpsid (const void *a, const void *b);  /* qsort comparison */

int main (int argc, char **argv) {

    int i, j, sidx = 0;
    student s[NSTD] = {{ .sid = 0 }};
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    printf ("\n enter student ID followed by grade (0-100)\n"
            " 101 for next student, 102 when finished.\n\n");

    for (; sidx < NSTD;) {  /* for each id up to max NSTD */

        int grade = 0, tries = 0;

        printf (" enter student ID: ");     /* enter/validate ID */
        while (scanf (" %d", &s[sidx].sid) != 1) {
            fprintf (stderr, " error: invalid student ID.\n");
            if (++tries == 3) {
                fprintf (stderr, " (max attempts reached, exiting.)\n");
                goto done;
            }
            printf (" enter student ID: ");
        }

        printf ("  Enter grades for student %d: ", s[sidx].sid);
        while (scanf (" %d", &grade) == 1) {    /* for each grade */
            if (grade < 0 || 102 < grade) {     /* invalid entry */
                fprintf (stderr, " warning: invalid entry '%d'.\n", grade);
                continue;
            }
            if (grade == 101) break;            /* next student */
            if (grade == 102) {                 /* exit input   */
                if (s[sidx].nmarks) sidx++;
                goto done;
            }

            s[sidx].marks[s[sidx].nmarks++] = grade;
            if (s[sidx].nmarks == NMARK) {
                fprintf (stderr, "  warning: max grades for ID %d.\n",
                        s[sidx].sid);
                break;
            }
        }
        sidx++;     /* increment student index */
    }
    done:;

    if (fp != stdin) fclose (fp);       /* close file if not stdin */

    qsort (s, sidx, sizeof *s, cmpsid); /* sort by student ID */

    for (i = 0; i < sidx; i++) {        /* output sorted data */
        printf ("\n Student ID : %3d     grades :", s[i].sid);
        for (j = 0; j < s[i].nmarks; j++)
            printf ("  %2d", s[i].marks[j]);
    }
    putchar ('\n');

    return 0;
}

/* integer comparison (use for all numeric types) */
int cmpsid (const void *a, const void *b)
{
    /* (a > b) - (a < b) prevents potential overflow */
    return (((student *)a)->sid > ((student *)b)->sid) - 
            (((student *)a)->sid < ((student *)b)->sid);
}

int cmpsid2 (const void *a, const void *b);
int cmpsid2 (const void *a, const void *b)
{
    student *s1 = (student *)a;  /* technically you should add 'const' */
    student *s2 = (student *)b;  /* omitted for simmplicity */

    /* comparing (a > b) - (a < b) prevents potential overflow */
    return (s1->sid > s2->sid) - (s1->sid < s2->sid);
}

示例输入

输入 10 名学生,每人 10 分:

$ cat dat/idmarks.txt
667 82 79 78 78 97 84 85 77 95 86 101
166 97 97 88 97 74 81 98 82 93 76 101
497 77 95 80 98 71 80 91 77 75 98 101
 31 82 78 79 95 92 86 87 95 91 83 101
515 76 76 93 87 71 72 77 92 72 93 101
145 81 79 95 91 99 93 95 99 83 77 101
517 92 98 96 75 83 89 95 93 75 95 101
836 97 84 98 98 91 98 88 71 90 75 101
381 82 98 76 81 90 72 93 93 76 86 101
 11 92 86 96 88 78 91 92 76 87 82 102

使用/输出示例

$ ./bin/studentmarks <dat/idmarks.txt

 enter student ID followed by grade (0-100)
 101 for next student, 102 when finished.

 <snip prompts>

 Student ID :  11     grades :  92  86  96  88  78  91  92  76  87  82
 Student ID :  31     grades :  82  78  79  95  92  86  87  95  91  83
 Student ID : 145     grades :  81  79  95  91  99  93  95  99  83  77
 Student ID : 166     grades :  97  97  88  97  74  81  98  82  93  76
 Student ID : 381     grades :  82  98  76  81  90  72  93  93  76  86
 Student ID : 497     grades :  77  95  80  98  71  80  91  77  75  98
 Student ID : 515     grades :  76  76  93  87  71  72  77  92  72  93
 Student ID : 517     grades :  92  98  96  75  83  89  95  93  75  95
 Student ID : 667     grades :  82  79  78  78  97  84  85  77  95  86
 Student ID : 836     grades :  97  84  98  98  91  98  88  71  90  75

查看代码以及其他答案,如果您有任何问题,请告诉我。选择你的结构内容,让它适合你,它应该让生活更轻松。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-31
    • 1970-01-01
    • 1970-01-01
    • 2020-11-25
    相关资源
    最近更新 更多