试图在结构使用scanf函数时分段故障(Segmentation Fault when trying

2019-06-23 18:31发布

我很新的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我得到一个错误也是如此。

有什么不对的代码?

Answer 1:

首先,不要担心 - 挫折是正常的开端C :)

既然你说你是一个初学者,我写了一个很长的答案,说明您可能要进行一些其他方面的改进。 很抱歉,如果我讨论一些事情,你已经知道了。 这里有一个总结:

  1. 你需要分配一些空间,为char* s到指向(这是什么导致的崩溃)
  2. 请务必检查从malloc的返回值
  3. 确保问scanf()为只读尽可能多的字符,你可以在你的字符串持有。
  4. 无需通过malloc将返回值。
  5. 记住free()否则您的malloc-ED。

你需要分配一些空间,为char* s到指向

在C中, char*意思是“一个指向一个char”。 char*常用于字符串,因为你可以索引指针像他们的阵列-例如,假设:

 char *a = "Hello";

然后, a[1]是指“第一char之后的char指向a ,在这种情况下'e' ;

你有这样的代码:

contactInfo *contact;
contact = (contactInfo *) malloc (sizeof(contactInfo));

在这一点上,你声明的指针,CONTACTINFO结构和分配大小合适的内存给它。 然而,结构内部的指针,目前没有指向任何东西-所以你的程序崩溃时,它调用scanf() 你还需要为你即将读,例如角色分配空间:

contact->fName = malloc(sizeof(char) * 10);

将10个字符分配空间。 你需要为每一个做到这一点char*的结构。

一对夫妇旁白的,我不想让你担心太多:

  • 在C, sizeof(char)始终是1,所以你可以写malloc(10)但在我看来这是不太可读。
  • 你也可以这样做:

     contact->fName = malloc(sizeof(*(contact->fName)) * 10); 

    这是稳健的类型变化fName -你总是因为种种的10分配足够的空间fName点。

请务必检查从malloc的返回值

回到现在的轨道上-你也应该检查从返回值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字符在fNamescanf()可能会读太多的字符。 您可以显式地告诉scanf函数的极限通过写"%Ns" ,其中N是在你的字符串中的字符的最大数量(减1为空终止末)。 所以,如果你已经分配了10个字符,那么你应该写:

scanf("%9s", contact->fName);

无需通过malloc将返回值。

最后一点- 你不需要投用C的malloc的返回值 ,所以我可能会写:

 contact =  malloc (sizeof(contactInfo));

记住free()什么你malloced

您可能已经这样做了,但每一次你malloc()什么,请确保您有相应free()在你的代码,一旦你用它做。 这告诉操作系统它可以有记忆回来了。 所以,如果你有什么地方

 contact =  malloc (sizeof(contactInfo));

后来,当你与该联系人完成的,你需要有类似:

 free(contact);

以避免内存泄漏。

只要你释放的东西,你不能访问它了。 所以,如果你已经在接触中malloced串,你必须先释放他们:

 free(contact->fName);  // doing this in the other order might crash
 free(contact);  

有几件事情要记住免费:

  1. 你不能释放任何的两倍。 为了避免这种情况,一个好的做法是这样写:

      if(contact != NULL) free(contact); contact = NULL; 

    如果你写这样,那么你就需要在创建他们也初始化所有的指针为NULL。 当您创建在他们的指针结构,一个简单的方法来做到这一点是使用calloc()代替malloc()来创建结构,因为calloc()返回存储器,它始终是零。

  2. 当你的程序退出时,所有的内存被释放回操作系统。 这意味着你没有在技术上需要free()的东西都是围绕对程序的生命周期。 不过,我建议你进入释放你malloced一切的习惯,否则你会忘记有一天,当这一点很重要。


进一步改进

作为一个评论者指出了另一个答案,使用幻数(代码中的硬编码数)一般是不好的做法。 在我给你上面的例子中,我硬编码“10”到程序的字符串的大小。 然而,这是更好地做一些事情,如:

#define FNAME_MAX_LENGTH 10

再后来去:

malloc(sizeof(char) * FNAME_MAX_LENGTH);

这样做的好处是,如果你需要的任何地方改变字符串的大小,你可以在一个地方改变它。 它还可以防止你不小心在一个地方输入100或1,造成潜在的严重,难以发现错误。

当然,现在你已经有了一个#define为长度,你需要更新scanf()调用我们指定的长度。 然而,由于scanf()需要的长度- 1,您将无法使用#define指定长度(至少,不能以任何好的可读的方式)。

因此,你可能会感兴趣的fgets()它读取了特定长度-1(或直到该行结束-以先到为准)。 然后,你可以这样做:

fgets(contact->fName,FNAME_MAX_LENGTH,stdin);

而不是scanf()调用。 另一个很好的理由让这个变化是scanf()可以一种痛苦 。

那么,除了上面的总结:

  1. 使用该字符串的长度的#define可避免的问题,并使其更容易在以后更改您的代码。
  2. fgets()是更容易使用比scanf() ,并且与使用更兼容#define为字符串长度。


Answer 2:

按照从@AdamMihalcin的建议,我给一个几乎完整的代码,我希望能为您服务作为参考。

几点需要注意:

  1. 的返回值malloc()应该进行检查。 因为malloc()从堆获取内存,如果没有足够的内存,那么malloc()可能返回NULL 。 要了解更多关于malloc ,你可以阅读它的手册页- man malloc

  2. 所有malloc “版内存必须是free ”版。

  3. scanf()的和与fgets()之间的区别和Ç - scanf()的VS得到()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;
}


Answer 3:

你应该对所有分配内存char *在你的结构。

例如:

contact->fName =  malloc(sizeof(char) * 10);

此外,你应该检查返回值malloc()



文章来源: Segmentation Fault when trying to use scanf on a struct