C语言中的文件,指针访问,文件读写

4

我正在尝试使用C语言编写一个跟踪学生借阅书籍的程序。在访问文件指针方面遇到了困难。当我使用文件时,我通常不使用fscanf(),而是使用普通的scanf。我有以下数据结构:

typedef struct{
   char fName[24], mInitial, lName[16];
}nameType;

typedef struct{
   unsigned long idNo;
   nameType studName;
   char course[8];
   int yrLevel;
   books borrowedBooks;
   int bksCtr;
}student;

typedef struct{
   student *studs;
   int studCtr;
}studList;

我目前已经创建了两个函数,一个是addStudToFile(void),用于向文件中添加学生,另一个是displayStudsFromFile(void),基本上是打印在文件中添加的学生。以下是我新手函数代码:

void addStudToFile(void)
{

   FILE *fp;
   studList myStud;


   fp = fopen("students.db", "w");
   if(fp!=NULL){
      /* ask for student details and adds these to the file */
      printf("Enter ID number: ");
      fflush(stdin);
      scanf(,"%lu", &myStud.studs->idNo);
      printf("Enter First Name: ");
      fflush(stdin);
      gets(myStud.studs->studName.fName);
      printf("Enter Last Name: ");
      fflush(stdin);
      gets(myStud.studs->studName.lName);
      printf("Enter Middle Initial: ");
      fflush(stdin);
      scanf("%c", &(myStud.studs->studName.mInitial));
      printf("Enter Course: ");
      fflush(stdin);
      gets(myStud.studs->course);
      printf("Enter Year: ");
      fflush(stdin);
      scanf("%d", &(myStud.studs->yrLevel));
      fwrite(&myStud, sizeof(studList),1,fp);
      fclose(fp);
   }
}

并且

void displayStudsFromFile(void)
{

   FILE *fp;
   studList myStud;

   fp = fopen("students.db", "r");
   if(fp!=NULL){
       while (fread(&myStud, sizeof(studList), 1, fp)){
           printf("%lu\t %s, %s %s\t %s-%d", myStud.studs->idNo, myStud.studs->studName.lName,
                                             myStud.studs->studName.fName, myStud.studs->studName.mInitial,
                                             myStud.studs->course, myStud.studs->yrLevel);
           printf("borrowed %d books", myStud.studs->bksCtr);
       }  
       fclose(fp);
   }
}
现在,我的问题在于访问我的列表,即myStud。在我的addStudToFile()函数中,每次我输入我的ID号码时,我的程序就会停止工作。为什么会停止工作?我需要malloc一些东西吗?还是我的scanf()访问方式不对?另一个情况是,当我调用我的显示函数时,我再次遇到了程序停止工作的情况。它会显示一些奇怪/垃圾值。

这里是我在扫描函数中遇到问题的屏幕截图:

enter image description here

这是我的显示函数:

enter image description here

希望有人能帮助我解决这个问题。谢谢!


不要因为没有发布图片而感到难过——这是一个纯文本程序,对吧?图片能添加什么呢? - Jongware
一个截图而已。抱歉,我已经编辑过了。谢谢!@Jongware - Claude Rhay
这行代码:while (fread(&myStud, sizeof(studList), 1, fp)){ 不一定会在到达文件结尾时停止,因为它可能会返回除了'1'以外的其他数字,例如在某些错误情况下。建议改为:while (1 == fread(&myStud, sizeof(studList), 1, fp)) ) { - user3629249
5个回答

3
你的直觉是正确的,你确实需要分配一些内存:)
typedef struct{
   student *studs;
   int studCtr;
}studList;

您的问题在于,您将studs定义为指向student结构体的指针,但实际上您没有为其分配任何内存,因此您无法使用->运算符来引用它。

您可以预先允许一定数量的条目,这样您就可以像以下方式定义studs

student studs[10];

为允许10个条目,或者在addStudToFile()中你可以要求用户输入他想要提供的条目数。在这种情况下,您将保留定义不变,并在获得用户输入后执行以下操作:

myStud.studs = (student *) malloc( sizeof(student) * how_many );

你所发布的代码中可能存在更多的错误,但目前以上问题是阻碍你前进的主要原因。

编辑:如果你选择使用malloc(),在从addStudToFile()返回之前,无论出于什么原因,你都应该确保调用

free(myStud.studs);

要不你就会出现内存泄露...

更新

好的,往下看,当你fwrite()所有内容时,请记住,你为studs malloc()了内存。sizeof(studlist)在编译时计算,并且不可能知道在运行时使用的附加内存。此外,这两个内存区域不能保证连续,因此,一个fwrite将无法完成。

按照你的代码结构,最好先fwrite() studCtr,然后是为studs分配的内存。

对于displayStudsFromFile(),既然只有一个循环,而没有真正存储以供以后使用,我只会使用

student myStud;

在这种情况下,您只需使用一个学生结构的实例而不是一个studlist。在此方案中,您可以执行一次fread()以从磁盘文件中读取studCtr,然后使用该计数器循环fread(),一次将一个学生对象读入myStud中。在该循环内,您可以按以下方式打印感兴趣的字段:

printf("borrowed %d books", myStud.bksCtr);

希望这能帮助你... C语言的初步学习可能有些困难 :D

我尝试使用malloc,但是在编译程序时出现了错误。它说“从'void'到'student'的转换无效”。我甚至不明白那个错误。 - Claude Rhay
void* 转换为 student,你在 malloc() 之前添加了 (student) 的转换吗?我后来进行了编辑。 - kostas
我的malloc起作用了,哈!谢谢!你能告诉我关于我的显示函数吗?那也是我卡住的地方。每次调用我的显示函数时它都会停止我的程序。 - Claude Rhay
我又有一个问题! @kostas ,在我放置了malloc函数之后,我尝试查看我的文件,但是里面什么也没有:/只有这些垃圾值ဨY〘@。 - Claude Rhay
我再次修改了我的答案,希望这能更好地帮助你理解你的代码出了哪些问题。 - kostas

1

myStud.studs是一个指向学生的指针,但我没有看到你实际分配该学生的地方。在执行&myStud.studs->idNo之类的操作之前,您需要先使用malloc分配一个学生。


我的malloc应该怎么写?myStud = (studList)malloc(sizeof(?)) - Claude Rhay
1
在C语言中,有几个原因不建议对malloc函数族的任何返回值进行强制类型转换。因此建议将其移除。 - user3629249

0
along with the problems mentioned already,
this function has its' own set of troubles.
I have inserted '<--' and a comment at each problem 

fflush(stdin) though works on some implementations, it's still undefined behaviour. 
According to the standard, fflush only works with output/update streams
( for your code, since the printf format strings do not end in '\n'
(  which would have forced the actual output to occur
(  change these lines to 'fflush(stdout)'

A ' ' in a scanf() format string will consume any white space found at that
point in the input.  Therefore, for almost all cases, the first char in
the format string should be: ' '.  Then newlines, spaces, etc 
will be consumed, as if they were never there.  It is even correct to 
use the leading ' ' when there is no white space to consume.

gets() is depreciated and will corrupt/overrun a input buffer, so NEVER 
use gets, rather, use fgets(), where the amount of input can be limited
and similar good things.

void addStudToFile(void)
{

   FILE *fp;
   studList myStud;


   fp = fopen("students.db", "w");
   if(fp!=NULL)
   {
      /* ask for student details and adds these to the file */

      printf("Enter ID number: ");
      fflush(stdin);  <-- change to stdout
      scanf(,"%lu", &myStud.studs->idNo); 
      <-- change format string to: " %lu"
      <-- add check of returned value to assure operation successful

      printf("Enter First Name: ");
      fflush(stdin); <-- change to stdout
      gets(myStud.studs->studName.fName); 
      <-- replace gets with fgets() +appropriate parms)
      <-- add check of returned value to assure operation successful

      printf("Enter Last Name: ");
      fflush(stdin); <-- change to stdout
      gets(myStud.studs->studName.lName); 
      <-- replace gets with fgets() +appropriate parms)
      <-- add check of returned value to assure operation successful

      printf("Enter Middle Initial: ");
      fflush(stdin); <-- change to stdout
      scanf("%c", &(myStud.studs->studName.mInitial)); 
      <-- replace format string with " %c"
      <-- add check of returned value to assure operation successful

      printf("Enter Course: ");
      fflush(stdin); <-- change to stdout
      gets(myStud.studs->course);
      <-- replace gets with fgets() +appropriate parms
      <-- add check of returned value to assure operation successful

      printf("Enter Year: ");
      fflush(stdin); <-- change to stdout
      scanf("%d", &(myStud.studs->yrLevel));
      <-- change format string to: " %d"
      <-- add check of returned value to assure operation successful

      fwrite(&myStud, sizeof(studList),1,fp);
      <-- add check of returned value to assure operation successful

      fclose(fp);
   <-- add else clause so use knows what happened. I.E.
       } else { perror( "fopen failed for write");  exit(EXIT_FAILURE); 
   } // end if
} // end function: addStudToFile

0
Here are my comments, prefixed by '<--'



void displayStudsFromFile(void)
{

   FILE *fp;
   studList myStud;

   fp = fopen("students.db", "r");
   if(fp!=NULL)
   {
       while (fread(&myStud, sizeof(studList), 1, fp))
       <-- add check of returned value to assure operation successful

       {
           printf("%lu\t %s, %s %s\t %s-%d", 
                  myStud.studs->idNo, 
                  myStud.studs->studName.lName,
                  myStud.studs->studName.fName, 
                  myStud.studs->studName.mInitial,
                  myStud.studs->course, 
                  myStud.studs->yrLevel);
           printf("borrowed %d books", myStud.studs->bksCtr);
       }  
       fclose(fp);
   <-- to let user know about error
   <-- insert: }else{ perror( "fopen failed for read"); exit(EXIT_FAILURE); 
   } // end if
} // end function: displayStudsFromFile

0
简而言之,不要编写指向文件的指针,否则它们以后将毫无意义。
典型的方法是首先写出项目的计数,然后循环遍历列表中的每个项目并逐个写出它们。
在读取器端:
1. 读取项目数量。 2. 分配足够的内存来容纳所有项目。 3. 逐个读取每个项目。

我理解,但我们的教练告诉我们现在在使用文件时要采用这种方法。 - Claude Rhay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接