Nov*_*ane 3 c malloc struct scanf segmentation-fault
我对c很陌生,此刻我也非常沮丧.这是我的代码:
typedef struct {
char* fName;
char* lName;
char* pNum;
char* address;
char* email;
} contactInfo;
void addContact(){
contactInfo *contact;
contact = (contactInfo *) malloc (sizeof(contactInfo));
printf("\n[Add a contact]\nFirst Name: ");
scanf("%s", contact->fName);
printf("%s", contact->fName);
}
Run Code Online (Sandbox Code Playgroud)
出于某种原因,当我输入scanf的值时,它会给我一个分段错误.如果我尝试在contact-> fName前添加&,我也会收到错误.
代码有什么问题?
Tim*_*nes 13
首先,不要担心 - 开始C的沮丧是正常的:)
既然你说你是初学者,我写了一个很长的答案,解释了你可能想要做的其他一些改进.对不起,如果我介绍一些你已经知道的事情.这是一个总结:
char*s指定一些空间来指向(这是造成崩溃的原因)scanf()只读取字符串中可以容纳的字符数.free()你做过任何动画片的事情.char*s指定一些空间来指向在C中,a char*表示"指向char的指针".char*通常用于字符串,因为您可以索引指针,就像它们是数组一样 - 例如,假设:
char *a = "Hello";
Run Code Online (Sandbox Code Playgroud)
然后, a[1]意思是" 在这个案例中,charchar指向的第一个;a'e'
你有这个代码:
contactInfo *contact;
contact = (contactInfo *) malloc (sizeof(contactInfo));
Run Code Online (Sandbox Code Playgroud)
此时,您已经声明了一个指向contactInfo结构的指针,并为其分配了正确大小的内存.但是,结构中的指针当前没有指向任何东西 - 所以你的程序在调用时会崩溃scanf().您还需要为要阅读的字符分配空间,例如:
contact->fName = malloc(sizeof(char) * 10);
Run Code Online (Sandbox Code Playgroud)
将为10个字符分配空间.您需要为char*结构中的每个人执行此操作.
我不希望你担心的几个副作用:
sizeof(char)总是1,所以你可以写malloc(10),但在我看来,它的可读性较差.你也可以这样做:
contact->fName = malloc(sizeof(*(contact->fName)) * 10);
Run Code Online (Sandbox Code Playgroud)
这对于类型的变化很有用fName- 你总是会为10 fName个点分配足够的空间.
现在回到正轨 - 您还应该检查返回值malloc():
contact->fName = malloc(sizeof(char) * 10);
if(contact->fName == NULL) {
// Allocation failed
}
Run Code Online (Sandbox Code Playgroud)
在某些情况下,您可能能够从失败的分配中恢复(例如,尝试再次分配,但要求更少的空间),但要从以下开始:
contact->fName = malloc(sizeof(char) * 10);
if(contact->fName == NULL) {
printf(stderr,"Allocation of contact->fName failed");
exit(EXIT_FAILURE);
}
Run Code Online (Sandbox Code Playgroud)
可能没问题.许多程序员会编写一个包装器来malloc()检查这个错误,以便他们不再需要担心它.
scanf()在字符串中读取尽可能多的字符.请注意,一旦你分配了10个字符fName,scanf()可能会读取太多字符.您可以通过写入来明确告诉scanf限制,"%Ns"其中N是字符串中的最大字符数(结尾处的空终止符减去1).所以,如果你已经分配了10个字符,那么你应该写:
scanf("%9s", contact->fName);
Run Code Online (Sandbox Code Playgroud)
最后一点 - 你不需要在C中强制转换malloc的返回值,所以我可能会写:
contact = malloc (sizeof(contactInfo));
Run Code Online (Sandbox Code Playgroud)
free()任何你拍摄过的东西您可能已经这样做了,但每次做malloc()任何事情时,请确保free()在完成后您的代码中有相应的内容.这告诉操作系统它可以恢复内存.所以,如果你有某个地方
contact = malloc (sizeof(contactInfo));
Run Code Online (Sandbox Code Playgroud)
稍后,当您完成该联系后,您将需要具有以下内容:
free(contact);
Run Code Online (Sandbox Code Playgroud)
避免内存泄漏.
一旦你释放了某些东西,你就不能再访问了它.所以,如果你在联系人中使用了malloced字符串,你必须首先释放它们:
free(contact->fName); // doing this in the other order might crash
free(contact);
Run Code Online (Sandbox Code Playgroud)
关于免费的一些要记住的事情:
你不能两次免费.为了避免这种情况,一个好的做法是写:
if(contact != NULL) free(contact);
contact = NULL;
Run Code Online (Sandbox Code Playgroud)
如果以这种方式编写,那么在创建它们时,您还需要将所有指针初始化为NULL.当您创建具有指针的结构时,一种简单的方法是使用calloc()而不是malloc()创建结构,因为calloc()返回始终为零的内存.
程序退出时,所有内存都将释放回操作系统.这意味着您在技术上不需要free()在程序的整个生命周期中使用的东西.但是,我建议养成释放你所有东西的习惯,否则你会忘记有一天它很重要.
作为评论者指出另一个答案,使用幻数(代码中硬编码的数字)通常是不好的做法.在上面给出的例子中,我将"10"硬编码到程序中作为字符串的大小.但是,最好做以下事情:
#define FNAME_MAX_LENGTH 10
Run Code Online (Sandbox Code Playgroud)
然后去:
malloc(sizeof(char) * FNAME_MAX_LENGTH);
Run Code Online (Sandbox Code Playgroud)
这样做的好处是,如果您需要在任何地方更改字符串的大小,您可以在一个地方更改它.它还可以防止您意外地在一个地方输入100或1,从而导致潜在的严重,难以发现的错误.
当然,既然你有一个#define长度,你需要更新scanf()我们指定长度的调用.但是,由于scanf()需要长度为1,因此您无法使用#define指定长度(至少不能以任何可读的方式).
因此,您可能感兴趣fgets(),它读取指定的长度-1(或直到行的末尾 - 以先到者为准).然后你可以这样做:
fgets(contact->fName,FNAME_MAX_LENGTH,stdin);
Run Code Online (Sandbox Code Playgroud)
而不是scanf()电话.做出这种改变的另一个好理由是,这scanf()可能是一种痛苦.
所以,除了上面的摘要:
fgets()比字符串长度更容易使用scanf(),并且更容易使用#define.