我对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);
}
由于某些原因,当我输入scanf的值时,它会给我一个分段错误。如果我尝试添加&在联系人面前 - > fName我也会收到错误。
代码出了什么问题?
答案 0 :(得分:14)
首先,不要担心 - 开始C :)沮丧是正常的。
既然你说你是初学者,我写了很长的答案,解释了你可能想做的其他一些改进。对不起,如果我介绍一些你已经知道的事情。这是一个总结:
char*
分配一些空间以指向(这是造成崩溃的原因) scanf()
只读取您在字符串中可以容纳的字符数。free()
任何您已经购物的内容。char*
分配一些空间以指向在C中,char*
表示"指向char"的指针。 char*
通常用于字符串,因为您可以索引指针,就像它们是数组一样 - 例如,假设:
char *a = "Hello";
然后,a[1]
表示" char
指向的字符后面的第一个a
,在这种情况下为'e'
;
您有以下代码:
contactInfo *contact;
contact = (contactInfo *) malloc (sizeof(contactInfo));
此时,您已经声明了一个指向contactInfo结构的指针,并为其分配了正确大小的内存。但是,结构内部的指针目前并不指向任何内容 - 所以当程序调用{{1}}时,程序会崩溃。您还需要为您即将阅读的字符分配空间,例如:
scanf()
将为10个字符分配空间。您需要为结构中的每个contact->fName = malloc(sizeof(char) * 10);
执行此操作。
我不想让你担心太多的一些旁白:
char*
始终为1,因此您可以写sizeof(char)
,但在我看来,它的可读性较差。 您还可以执行以下操作:
malloc(10)
这对于contact->fName = malloc(sizeof(*(contact->fName)) * 10);
类型的更改非常有用 - 您总是会为10个fName
点分配足够的空间。
现在回到正轨 - 您还应该检查fName
的返回值:
malloc()
在某些情况下,您可以从失败的分配中恢复(例如,尝试 再次分配,但要求更少的空间),但首先要:
contact->fName = malloc(sizeof(char) * 10);
if(contact->fName == NULL) {
// Allocation failed
}
可能没事。许多程序员会为contact->fName = malloc(sizeof(char) * 10);
if(contact->fName == NULL) {
printf(stderr,"Allocation of contact->fName failed");
exit(EXIT_FAILURE);
}
编写一个包装器来执行此错误检查,以便他们不再需要担心它。
malloc()
读取字符串中可以容纳的字符数。请注意,一旦您在scanf()
中分配了10个字符,fName
可能会读取太多字符。您可以通过写scanf()
来明确告诉scanf限制,其中N是字符串中的最大字符数(最后为空终止符减去1)。所以,如果你已经分配了10个字符,那么你应该写:
"%Ns"
最后一点 - you don't need to cast the return value of malloc in C,所以我可能会写:
scanf("%9s", contact->fName);
contact = malloc (sizeof(contactInfo));
您提出的任何内容您可能已经在执行此操作,但每次free()
任何事情时,请确保在完成操作后代码中有相应的malloc()
。这告诉操作系统它可以恢复内存。所以,如果你有某个地方
free()
稍后,当您完成该联系后,您需要拥有以下内容:
contact = malloc (sizeof(contactInfo));
以避免内存泄漏。
只要您释放了某些内容,您就不能再访问它了。所以,如果您在联系人中使用了malloced字符串,那么您必须首先释放它们:
free(contact);
关于免费的一些事情需要记住:
你不能两次释放任何东西。为了避免这种情况,一个好的做法是写:
free(contact->fName); // doing this in the other order might crash
free(contact);
如果以这种方式编写,那么在创建它们时,您还需要将所有指针初始化为NULL。当您创建包含指针的结构时,一种简单的方法是使用calloc()
而不是 if(contact != NULL) free(contact);
contact = NULL;
来创建结构,因为malloc()
返回始终为零的内存。 / p>
当程序退出时,所有内存都将释放回操作系统。这意味着您不需要技术上需要calloc()
项目生命周期内的事情。但是,我建议养成释放你所提出的所有内容的习惯,因为否则你有一天会忘记它的重要性。
作为评论者指出另一个答案,使用魔术数字(代码中硬编码的数字)通常是不好的做法。在我上面给出的例子中,我已经硬编码" 10"作为字符串的大小进入程序。但是,做以下事情会更好:
free()
然后去:
#define FNAME_MAX_LENGTH 10
这样做的好处是,如果您需要在任何地方更改字符串的大小,您可以在一个地方更改它。它还可以防止您意外地在一个地方输入100或1,从而导致潜在的严重,难以发现的错误。
当然,既然您已经获得了malloc(sizeof(char) * FNAME_MAX_LENGTH);
的长度,那么您需要更新我们指定长度的#define
来电。但是,由于scanf()
需要长度为1,因此您无法使用scanf()
指定长度(至少不能以任何可读的方式)。
因此,您可能对fgets()
感兴趣,它读取指定的长度-1(或直到行的末尾 - 以先到者为准)。然后你可以这样做:
#define
而不是fgets(contact->fName,FNAME_MAX_LENGTH,stdin);
来电。进行此更改的另一个好理由是scanf()
可以是kind of a pain。
所以,除了上面的摘要:
scanf()
比fgets()
更容易使用,并且与使用scanf()
字符串长度更兼容。答案 1 :(得分:1)
根据@AdamMihalcin的建议,我提供了一个几乎完整的代码,希望能为您提供参考。
注意几点:
应检查malloc()
的返回值。因为malloc()
从堆中获取内存,并且如果内存不足,malloc()
可能会返回NULL
。要了解有关malloc
的更多信息,您可以阅读其手册页 - man malloc
所有malloc
'ed内存必须为free
。
Difference between scanf() and fgets()和C - scanf() vs gets() vs fgets()解释了为什么fgets()
优先于scanf()
代码如下:
#include <stdio.h>
#include <stdlib.h>
/*
define the length of each filed
in the contactInfo struct
*/
#define L_fName 10
#define L_lName 10
#define L_pNum 10
#define L_address 25
#define L_email 15
typedef struct {
char* fName;
char* lName;
char* pNum;
char* address;
char* email;
} contactInfo;
contactInfo * release_ci(contactInfo * contact)
{
if (contact == NULL) return NULL;
free(contact->fName);
free(contact->lName);
free(contact->pNum);
free(contact->address);
free(contact->email);
free(contact);
return NULL;
}
contactInfo * alloc_ci()
{
contactInfo *contact;
if ((contact = malloc(sizeof(contactInfo))) == NULL) {
printf("ERROR: unable to allocate memory for contactInfo \n");
goto free_and_fail;
}
if ((contact->fName = malloc(sizeof(char) * L_fName)) == NULL) {
printf("ERROR: unable to allocate memory for fName\n");
goto free_and_fail;
}
if ((contact->lName = malloc(sizeof(char) * L_lName)) == NULL) {
printf("ERROR: unable to allocate memory for lName\n");
goto free_and_fail;
}
if ((contact->pNum = malloc(sizeof(char) * L_pNum)) == NULL) {
printf("ERROR: unable to allocate memory for pNum\n");
goto free_and_fail;
}
if ((contact->address = malloc(sizeof(char) * L_address)) == NULL) {
printf("ERROR: unable to allocate memory for address\n");
goto free_and_fail;
}
if ((contact->email = malloc(sizeof(char) * L_email)) == NULL) {
printf("ERROR: unable to allocate memory for email\n");
goto free_and_fail;
}
return contact;
free_and_fail:
release_ci(contact);
return NULL;
}
int main()
{
contactInfo *ci = alloc_ci();
if (!ci) return -1;
printf("Enter fName : ");
fgets (ci->fName, L_fName, stdin);
printf("Enter lName : ");
fgets (ci->lName, L_lName, stdin);
printf("Enter pNum : ");
fgets (ci->pNum, L_pNum, stdin);
printf("Enter address : ");
fgets (ci->address, L_address, stdin);
printf("Enter email : ");
fgets (ci->email, L_email, stdin);
/* TODO: validation for all the input fields */
release_ci(ci);
return 0;
}
答案 2 :(得分:0)
您应该为结构中的所有char *
分配内存。
例如:
contact->fName = malloc(sizeof(char) * 10);
此外,您应该检查malloc()