我有一个结构:
typedef struct student
{
char fname[30];
char sname[30];
char tname[30];
Faculty fac;
int course;
char group[10];
int room;
int bad;
} Student;
我是从文件中读到的:
Database * dbOpen(char *fname)
{
FILE *fp = fopen(fname, "rb");
List *lst, *temp;
Student *std;
Database *db = malloc(sizeof(*db));
if (!fp)
return NULL;
FileNameS = fname;
std = malloc(sizeof(*std));
if (!fread(std, sizeof(*std), 1, fp)) {
db->head = db->tail = NULL;
return db;
}
lst = malloc(sizeof(*lst));
lst->s = std;
lst->prev = NULL;
db->head = lst;
while (!feof(fp)) {
fread(std, sizeof(*std), 1, fp);
temp = malloc(sizeof(*temp));
temp->s = std;
temp->prev = lst;
lst->next = temp;
lst = temp;
}
lst->next = NULL;
db->tail = lst;
fclose(fp);
return db;
}
我有一个问题...在最后一条记录中我有一个这样的文件指针: `FP 0x10311448 {_ptr = 0x00344b90“НННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННН_ _iobuf *
` 我读了2次最后的记录......
保存文件代码:
void * dbClose(Database *db)
{
FILE *fp = fopen(FileNameS, "w+b");
List *lst, *temp;
lst = db->head;
while(lst != NULL) {
fwrite(lst->s, sizeof(*(lst->s)), 1, fp);
temp = lst;
lst = lst->next;
free(temp);
}
free(db);
fclose(fp);
}
答案 0 :(得分:2)
您需要考虑以下几个观察到的问题:
(1)如果文件的字节长度不是sizeof(Student)
的倍数,该怎么办?然后这段代码:
while (!feof(fp)) {
fread(std, sizeof(*std), 1, fp);
...
可以将部分结构读入std
指向的内存中。 std
的内容将被部分填充,并且可能在内存中留下未终止的C字符串。
您必须检查fread()
的返回值。
(2)这是一个更重要的问题。您正在重新使用std
分配和指向的内存,尽管您将指针存储在链表的每个元素中。
您的意图似乎是为每个Student
记录分配新内存。在这种情况下,您可以在循环体内调用malloc()
,从文件中读取多条记录。
此错误取决于文件长度,也是 - 文件必须包含多个记录。
(3)您可以重新组织代码,以便在循环迭代期间读取第一条记录,以删除不必要的重复代码。
(4)您可以考虑使用calloc()
来确保您已初始化Student
的所有记录。
像这样:
Database * dbOpen(char *fname)
{
FILE *fp = fopen(fname, "rb");
List *lst, *temp;
Student *std;
Database *db = NULL;
if (!fp)
return db;
FileNameS = fname;
db = malloc(sizeof(*db));
db->head = NULL;
lst = NULL;
while (!feof(fp)) {
std = malloc(sizeof(*std));
if (!fread(std, sizeof(*std), 1, fp))
{
free(std);
fprintf(stderr, "Input file concludes in partial record.\n");
break;
}
temp = malloc(sizeof(*temp));
temp->s = std;
temp->prev = lst;
temp->next = NULL;
if (lst == NULL)
db->head = temp;
else
lst->next = temp;
lst = temp;
}
assert(lst->next == NULL ); /* Now performed above by temp->next assignement. */
db->tail = lst;
fclose(fp);
return db;
}
我没有编译和测试上面的代码,但它应该接近工作。注意如何添加一个特殊情况来初始化db->head
(第一次循环, lst 等于 NULL ),否则前一个列表元素被链接到新添加的元素(后来的迭代)。当然,我们也应该检查malloc()
的返回值,但为了清楚起见,这里省略了。
答案 1 :(得分:1)
这对我来说很突出:
while (!feof(fp)) {
fread(std, sizeof(*std), 1, fp);
temp = malloc(sizeof(*temp));
temp->s = std;
temp->prev = lst;
lst->next = temp;
lst = temp;
}
您不应将feof()
用作循环条件;文件结束指示器直到 之后才会尝试读取文件末尾,因此您的循环将执行太多次。重新构建循环以使用fread()
的返回值,并仅在feof()
失败时检查fread()
:
while (fread(std, sizeof *std, 1, fp) == 1)
{
temp = malloc(sizeof *temp);
temp->s = std;
...
}
if (feof(fp))
// handle end of file
else
// handle other read error
答案 2 :(得分:0)
第一次没有挖得太深,我会说你写完了你的记录结尾。您显示的输出看起来很像调试内存输出,您会看到跟踪malloc缓冲区。也许你的数据库没有正确对齐?现在再看一下......
答案 3 :(得分:0)
在这个循环中:
while (!feof(fp)) {
fread(std, sizeof(*std), 1, fp);
temp = malloc(sizeof(*temp));
temp->s = std;
temp->prev = lst;
lst->next = temp;
lst = temp;
}
您正在使用fread
的结果而不检查它是否已成功返回。
在假设数据成功读取之前,您应该检查feof(fp)
或fread
的返回值。