我的任务是编写一个文件,其中显示用户输入的未知数量的记录。每条记录有以下字段:名、姓、地址、城市、州、邮政编码和电话号码。
我认为最好的方法是用上面的字段定义一个结构体Record,然后声明一个Record
s数组,其中包含用户输入的记录数量。为了实现这一点,我将使用一个循环来获取每个记录的每个字段的输入,然后如果用户想要继续动态地在Record
数组中分配额外的空间,并继续直到用户输入no。我在
scanf("%s", records[i]->fname);
我的代码有什么问题?
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
struct Record;
struct Record
{
char fname[51];
char lname[51];
char address[51];
char city[51];
char state[51];
int zipcode;
int phoneNumber;
};
int main()
{
FILE *fileWriter;
const char filename[] = "data.txt";
char answer = 'y';
int size = 1;
int i = 0;
struct Record **records;
records = malloc(sizeof(*records)*(size));
while(answer == 'y' || answer == 'Y')
{
printf("First Name: n");
scanf("%s", records[i]->fname);
printf("Last Name: n");
scanf("%s", records[i]->lname);
printf("Address: n");
scanf("%s", records[i]->address);
printf("City: n");
scanf("%s", records[i]->city);
printf("State: n");
scanf("%s", records[i]->state);
printf("Zipcode: n");
scanf("%d", records[i]->zipcode);
printf("Phone Number: n");
scanf("%d", records[i]->phoneNumber);
//stores all record info
printf("Are there anymore records? [y/n] ");
answer = getchar();
if(answer == 'y' || answer == 'Y')
{
size++;
records[i++];
printf("n");
}
records = realloc(records,sizeof(*records)*(size));
}
//open file
fileWriter = fopen(filename,"wb");
if(fileWriter != NULL)
{
if(fwrite(records,sizeof(*records),size,fileWriter) != 1)
{
fprintf(stderr, "Failed to write to %sn", filename);
exit(1);
}
fclose(fileWriter);
}
else
{
printf("Error opening file.");
}
}
<<p> 编辑版本/strong> #include <stdio.h>
#include <stdlib.h>
#include <conio.h>
struct Record
{
char fname[51];
char lname[51];
char address[51];
char city[51];
char state[51];
int zipcode;
int phoneNumber;
};
int main()
{
FILE *fileWriter;
const char filename[] = "data.txt";
char answer = 'y';
int size = 1;
int i = 0;
struct Record *records = NULL;
struct Record *records_temp;
while(answer == 'y' || answer == 'Y')
{
struct Record *records_temp = realloc(records,(size)*sizeof(*records));
if(records_temp == NULL)
{
free(records);
}
records = records_temp;
printf("First Name: n");
scanf("%s", records[i].fname);
printf("Last Name: n");
scanf("%s", records[i].lname);
printf("Address: n");
scanf(" %[^n]", records[i].address);
printf("City: n");
scanf("%s", records[i].city);
printf("State: n");
scanf("%s", records[i].state);
printf("Zipcode: n");
scanf("%d", &records[i].zipcode);
printf("Phone Number: n");
scanf("%d", &records[i].phoneNumber);
//stores all record info
printf("Are there anymore records? [y/n] ");
answer = getchar();
if(answer == 'y' || answer == 'Y')
{
size++;
records[i++];
printf("n");
}
//open file
fileWriter = fopen(filename,"wb");
if(fileWriter != NULL)
{
if(fwrite(records,sizeof(*records),size,fileWriter) != 1)
{
fprintf(stderr, "Failed to write to %sn", filename);
exit(1);
}
fclose(fileWriter);
}
else
{
printf("Error opening file.");
}
}
}
嗯,你得到一个段错误,因为你没有为你的records
中的第一个实体分配内存。
要解决这个问题,你需要
records[size-1] = malloc(sizeof(Records));
这么说吧:records
是指向Records
的指针。
records = malloc(sizeof(*records)*(size));
你实际上要求size
指针指向Records
。但这还不够,你需要分配另一个内存来存储实际的Records
所以这就是为什么我们必须
records[size - 1] = malloc(sizeof(Records));
注意:如果size
> 1,那么你应该这样做:
int i = 0;
for(;i < size; i++) {
records[i] = malloc(sizeof(Records));
}
除此之外,为什么你要用Records **
,正如Arjun已经解释的那样,你应该使用Records *
并修复realloc
-ing新内存的部分,因为如果realloc
失败,它返回NULL
,你最终会在最坏的情况下出现内存泄漏或另一个段错误,无论哪种方式——这对你的程序都不好。
请参阅Arjun的帖子
当您想为Record
s列表动态分配空间时,您应该这样做:
struct Record *records;
records = malloc(size * sizeof(*records));
为Record
的size
个数分配空间。
要增加分配的大小,应该:
struct Record *records_temp = realloc(records, newsize * sizeof(*records));
if (records_temp == NULL) {
free(records);
/* die with error -ENOMEM */
}
records = records_temp;
不要对同一个指针进行realloc
操作。它可能导致您在失败时泄漏内存。
或,您可以通过最初提供NULL
指针来避免malloc()
并在循环中仅使用realloc()
。
c89标准规定:
4.10.3.4 realloc函数如果ptr是空指针,realloc函数的行为与malloc类似函数。
struct Record *records = NULL;
struct Record *records_temp;
size = INITIAL_SIZE;
while (/* your condition */) {
records_temp = realloc(records, size * sizeof(*records));
if (records_temp == NULL) {
free(records);
/* die with error -ENOMEM */
}
records = records_temp;
/* do stuff */
size += SIZE_INCREMENT;
}
正如Jonathan Leffler评论的那样,但他拒绝回答他的评论:
注意
records[i++];
行增加了i
,没有做任何其他有用的事情。
也:
还要注意,
struct Record;
行实际上是不必要的。唯一不同的是,如果您在函数作用域中而不是在文件作用域中定义相互递归的结构(这种用法是在文件作用域中)。事实上,这一行说的是"有一个类型struct Record
",下一段代码说的是"有一个类型struct Record
,这就是它的定义方式"。
当Cool Guy问他这是什么意思时,Jonathan说:
struct A { … }; struct B { … }; void f(void) { struct A; struct B { …; struct A *a_ref; … }; struct A { …; struct B *b_ref; … }; … }
如果没有
struct A;
行,a_ref
元素将指向外部定义类型struct A
的结构,而不是相互递归的结构类型对。错误消息也可能非常令人困惑!然而,像这样重用类型名是一个坏主意。