【问题标题】:Reading file data to array of structures while allocating memory dynamically在动态分配内存的同时将文件数据读取到结构数组
【发布时间】:2014-07-19 05:46:14
【问题描述】:

我需要帮助解决以下代码。使用 gcc 编译代码后,它可以像 ./compiledFile inputFile.txt 一样运行它应该读取 inputFile.txt,同时为每个变量动态分配内存,在这种情况下为 name 和 courseID,但我的代码不起作用。我最不了解且需要帮助的地方是分配内存,将数据插入结构并打印数据,如下面的示例。通过查看这段代码,您可以看出我是 c 和动态内存分配和结构的新手。

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

struct people
{
  char* name[10];
  char* courseID[15];
  int grade;
};

void printData(struct people student[], int count);
int main(int argc, char* argv[])
{
  FILE *in_file;
  char buffer[30];
  char *token, *del=",";
  int count=0;

  struct people student[20];

  if(( in_file = fopen(argv[1], "r")) == NULL)
  {
     printf("unable to open the file");
  }
  while (fgets(buffer, sizeof(buffer), in_file))
  {
     student = malloc(sizeof(struct people));
     token = strtok(buffer, del);
     strcpy(student[count].name, token);
     count++;
  }
  fclose(in_file);
  printData(student, count);
}
void printData(struct people student[], int count)
{
  int i;
  for(i=0; i<count; i++)
  {
    printf("%s", student[i].courseID);
    if (strcmp((student[i].name, student[i].courseID) > 0))
    {
      printf("%s  %s", student[i].name, student[i].grade)
    }
  }
}

data.txt 文件包含以下内容,以逗号分隔:

John,MATH 1324,90 David,SCI 1401,88 Omondi,MATH 1324,89 David,MATH 1324,90

打印出来后应该如下所示:

MATH 1324
John 90
Omondi 89
David 90

SCI 1401
David 88

【问题讨论】:

  • struct student ? 'student = malloc(sizeof(struct student))' 你的意思是student = malloc(sizeof(struct people))
  • 抱歉错字应该是 struct people
  • 你用这句话弄丢了我:if (strcmp((student[i].name, student[i].courseID) &gt; 0)) 意图是什么?
  • 我试图比较学生是否有 courseID 以在 courseID 下打印它们,但我对如何实现它有点迷茫。

标签: c memory-management dynamic-memory-allocation


【解决方案1】:

首先,如果您还可以分享您在运行此程序时得到的实际输出或错误,那就太好了。

大多数时候我们不知道数据元素的实际大小时使用动态内存分配,但是这里你已经将struct people student 的大小固定为20

 struct people student[20];

这绝对没问题,但是你在 while 循环中执行 malloc

     student = malloc(sizeof(struct student);

您已经使用数组声明分配了 20 个位置,现在不需要 malloc。 如果你想通过指针来使用动态内存分配来进行学习,那么你应该首先将 student 声明为指向 struct people 类型的指针

struct people* student;

在while循环中动态分配内存

student=(struct people*) malloc(sizeof(struct people));

然后访问它

*(student+count)

希望这会有所帮助,如果您仍有疑问/问题,请编辑问题并包含您在编译/运行此程序时得到的输出/错误。

【讨论】:

    【解决方案2】:

    问题代码的几个问题...

    1) main()的定义:

    int main(int argc, char* argv[])
    

    它必须返回一个整数。在 main() 的末尾添加一个 return 语句,并制作一个适当的“CLEANUP”部分:

       printData(student, count);
    
    CLEANUP:
    
       if(in_file)
          fclose(in_file);
    
       return(0);
    }
    

    2) 更好地处理 fopen() 错误情况:

      if(( in_file = fopen(argv[1], "r")) == NULL)
      {
         printf("unable to open the file");
         goto CLEANUP;
      }
    

    并且,初始化in_file指针:

    int main(int argc, char* argv[])
    {
      FILE *in_file = NULL;
    

    3) 接下来,需要建立student 的确切定义。它是静态数组,还是指向动态分配数组的指针?我将假设您想使用动态数组(给定问题文本)。但是,此假设与以下行冲突,它将student 定义为静态数组:

      struct people student[20];
    

    改成:

      struct people *student = NULL;
    

    4) 现在,以下问题代码为每个学生分配一个新的(单独的)内存块:

         student = malloc(sizeof(struct people));
    

    但是,我们需要的是一个数组中的所有学生记录,在同一块内存中。因此,我们需要扩展一块内存,以便在读取学生记录时包含这些记录,如下所示:

      while (fgets(buffer, sizeof(buffer), in_file))
      {
         void *tmp = realloc(student, sizeof(struct people) * (count + 1));
         if(NULL == tmp)
         {
            printf("realloc() failed.\n");
            goto CLEANUP;
         }
         student = tmp;
    
         token = strtok(buffer, del);
    

    5) 看看人员结构:

    struct people
    {
      char* name[10];
      char* courseID[15];
      int grade;
    };
    

    问题代码在指针和数组方面似乎有些困难。该代码试图将 name 和 courseID 字段定义为指针和数组。鉴于问题与动态分配内容有关,我选择朝那个方向发展。因此,此结构应更改为:

    struct people
    {
      char *name;
      char *courseID;
      int   grade;
    };
    

    6) 因此,每次循环时,学生姓名都会被放置在分配的存储空间中,并由 .name 字段指向。所以,改变这个:

         token = strtok(buffer, del);
         strcpy(student[count]->name, token);
         count++;
      }
    

    到这里:

         token = strtok(buffer, del);
         student[count].name = strdup(token);
         count++;
      }
    

    7) 我不明白这行的意图:

       if (strcmp((student[i].name, student[i].courseID) > 0))
    

    我倾向于消除它。


    8) 以下行有缺陷:

          printf("%s  %s", student[i].name, student[i].grade)
    

    将其更改为打印整数grade(并且不要忘记结束分号):

          printf("%s  %d\n", student[i].name, student[i].grade);
    

    '\n' 使输出看起来更好,每行一条记录。


    9) 由于student 是指向动态分配内存的指针(不是静态数组),因此更改:

    void printData(struct people student[], int count)
    

    到这里:

    void printData(struct people *student, int count)
    

    10) 现在,完成解析数据的任务;从此:

         token = strtok(buffer, del);
         strcpy(student[count].name, token);
         count++;
      }
    

    到这里:

         token = strtok(buffer, del);
         student[count].name = strdup(token);
         token = strtok(NULL, del);
         student[count].courseID = strdup(token);
         token = strtok(NULL, del);
         student[count].grade = strtol(token, NULL, 10);
         count++;
      }
    

    11) 现在,为了让生活更轻松,对数组进行排序。首先是 courseID,然后是名称:

     ...  
         count++;
      }
    
      /** Sort the array by coursID, then by name. **/
      qsort(student, count, sizeof(*student), CmpStudentRecs);
    
      printData(student, count);
     ...
    

    这需要额外的“比较学生记录”功能:

    int CmpStudentRecs(const void *recA, const void *recB)
    {
      int result = 0;
      struct people *stuRecA = (struct people *)recA;
      struct people *stuRecB = (struct people *)recB;
    
      /** First compare the courseIDs **/    
      result=strcmp(stuRecA->courseID, stuRecB->courseID);
    
      /** Second (if courseIDs match) compare the names **/
      if(!result)
         result=strcmp(stuRecA->name, stuRecB->name);
    
      return(result);
    }
    

    12) printData() 函数的一些收尾工作:

    void printData(struct people *student, int count)
    {
      int i;
      char *courseID = "";
    
      for(i=0; i<count; i++)
      {
        if(strcmp(courseID, student[i].courseID))
          {
          printf("%s\n", student[i].courseID);
          courseID = student[i].courseID;
          }
    
        printf("\t%s  %d\n", student[i].name, student[i].grade);
      }
    }
    

    完成。输出:

    SLES11SP2:~/SO> ./test data.txt
    MATH 1324
       David  90
       John  90
       Omondi  89
    SCI 1401
       David  88
    SLES11SP2:~/SO> 
    

    SPOILER

    【讨论】:

    • 我做了类似student = malloc(20 * sizeof(struct people)); 然后我做了student-&gt;name = malloc(10 * sizeof(char));student-&gt;courseID = malloc(15 * sizeof(char)); 为了分配内存来保存字符串,但是当我运行它时出现分段错误
    • @GeekyCoder,使用strdup() 可能比malloc() 更容易。看看 SPOILER 链接(在答案的底部)。
    【解决方案3】:
    1. people的定义改为:

      struct people
      {
        char name[10];
        char courseID[15];
        int grade;
      };
      

      这假定 name 不会超过 9 个字符,coursID 不会超过 14 个字符。如果不是这样,请相应地更改它们。

    2. 行:

      student = malloc(sizeof(struct student);
      

      在几个方面是错误的。

      • student 已声明为 people 的数组。你不能将它分配给malloc分配的内存。

      • struct student 不是类型。

      该行可以删除。

    3. 线

      strcpy(student[count].name, token);
      

      如果token 的长度大于10(或您在people 中为name 选择的任何大小),可能会导致问题。更安全的做法是使用strncpy

      strncpy(student[count].name, token, 10);
      student[count].name[9] = '\0';
      
    4. 您尚未在任何地方设置courseID 的值。然而,您正试图在printData 中打印它。您正在为grade 做同样的事情。您需要更新输入行的处理以正确设置。

      while 循环更改为:

      while (fgets(buffer, sizeof(buffer), in_file))
      {
         token = strtok(buffer, del);
         strncpy(student[count].name, token, 10);
         student[count].name[9] = '\0';
         token = strtok(NULL, del);
         strncpy(student[count].courseID, token, 15);
         student[count].courseID[14] = '\0';
         token = strtok(NULL, del);
         student[count].grade = atoi(token);
         count++;
      }
      
    5. printData 中有几个语法错误。但是,修复这些语法错误并不能满足您的打印要求。如果您对数据进行排序,将更容易按您想要的顺序打印数据。以下函数将帮助您对数据进行排序。

      int compareStudent(const void* ptr1, const void* ptr2)
      {
         struct people* p1 = (struct people*)ptr1;
         struct people* p2 = (struct people*)ptr2;
         return (strcmp(p1->courseID, p2->courseID));
      }
      
      void sortData(struct people student[], int count)
      {
         qsort(student, count, sizeof(struct people), compareStudent);
      }
      

      您可以在拨打printData 之前拨打sortData 或先拨打sortDataprintData 中。打印数据的逻辑需要稍微更新一下。这是更新后的printData

      void printData(struct people student[], int count)
      {
        int i;
        int j;
      
        sortData(student, count);
      
        for(i=0; i<count; ++i)
        {
          printf("%s\n", student[i].courseID);
          printf("%s %d\n", student[i].name, student[i].grade);
          for ( j = i+1; j < count; ++j )
          {
             if (strcmp(student[i].courseID, student[j].courseID) == 0)
             {
                printf("%s %d\n", student[j].name, student[j].grade);
             }
             else
             {
                i = j-1;
                break;
             }
          }
        }
      }
      

    【讨论】:

    • 如果我想使用 malloc 进行内存分配,我该怎么做
    • 我这样做了,成绩应该是一行 0 并且名称和 courseID 没有打印出来
    【解决方案4】:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct people {
        char name[10];//char *name[10] is array of pointer 
        char courseID[15];
        int grade;
    };
    
    void printData(struct people student[], int count);
    
    int main(int argc, char* argv[]){
        FILE *in_file;
        char buffer[30];
        char *token, *del=",";
        int count=0;
    
        struct people student[20];
    
        if((in_file = fopen(argv[1], "r")) == NULL){
            printf("unable to open the file");
            return -1;//It is not possible to continue the process
        }
    
        while (fgets(buffer, sizeof(buffer), in_file)){
            //student = malloc(sizeof(struct people));//It is by securing an array already
            token = strtok(buffer, del);
            strcpy(student[count].name, token);
            token = strtok(NULL, del);
            strcpy(student[count].courseID, token);
            token = strtok(NULL, del);
            student[count].grade = atoi(token);
            count++;
        }
        fclose(in_file);
        printData(student, count);
    }
    
    int cmp(const void *a, const void *b){
        const char *x = ((const struct people*)a)->courseID;
        const char *y = ((const struct people*)b)->courseID;
        return strcmp(x, y);
    }
    
    void printData(struct people student[], int count){
        qsort(student, count, sizeof(struct people), cmp);//sort by courseID
        char *prev = "";
        int i;
        for(i=0; i<count; i++){
            if(strcmp(prev, student[i].courseID)!=0){
                prev = student[i].courseID;
                printf("\n%s\n", prev);
            }
            printf("%-9s %d\n", student[i].name, student[i].grade);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-11-13
      • 2021-06-09
      • 2020-04-18
      • 1970-01-01
      • 2013-12-25
      • 2012-04-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多