尝试在结构上使用scanf时出现分段错误

时间:2012-04-19 04:12:05

标签: c struct segmentation-fault malloc scanf

我对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我也会收到错误。

代码出了什么问题?

3 个答案:

答案 0 :(得分:14)

首先,不要担心 - 开始C :)沮丧是正常的。

既然你说你是初学者,我写了很长的答案,解释了你可能想做的其他一些改进。对不起,如果我介绍一些你已经知道的事情。这是一个总结:

  1. 您需要为char*分配一些空间以指向(这是造成崩溃的原因)
  2. 确保检查来自malloc的返回值
  3. 请确保让scanf()只读取您在字符串中可以容纳的字符数。
  4. 无需从malloc转换返回值。
  5. 请记住free()任何您已经购物的内容。

  6. 您需要为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); 执行此操作。

    我不想让你担心太多的一些旁白:

    • 在C中,char*始终为1,因此您可以写sizeof(char),但在我看来,它的可读性较差。
    • 您还可以执行以下操作:

      malloc(10)

      这对于contact->fName = malloc(sizeof(*(contact->fName)) * 10); 类型的更改非常有用 - 您总是会为10个fName点分配足够的空间。

    确保检查malloc

    的返回值

    现在回到正轨 - 您还应该检查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"

    无需从malloc转换返回值。

    最后一点 - 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);
    

    关于免费的一些事情需要记住:

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

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

      如果以这种方式编写,那么在创建它们时,您还需要将所有指针初始化为NULL。当您创建包含指针的结构时,一种简单的方法是使用calloc()而不是 if(contact != NULL) free(contact); contact = NULL; 来创建结构,因为malloc()返回始终为零的内存。 / p>

    2. 当程序退出时,所有内存都将释放回操作系统。这意味着您不需要技术上需要calloc()项目生命周期内的事情。但是,我建议养成释放你所提出的所有内容的习惯,因为否则你有一天会忘记它的重要性。


    3. 进一步改进

      作为评论者指出另一个答案,使用魔术数字(代码中硬编码的数字)通常是不好的做法。在我上面给出的例子中,我已经硬编码" 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

      所以,除了上面的摘要:

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

答案 1 :(得分:1)

根据@AdamMihalcin的建议,我提供了一个几乎完整的代码,希望能为您提供参考。

注意几点:

  1. 应检查malloc()的返回值。因为malloc()从堆中获取内存,并且如果内存不足,malloc()可能会返回NULL。要了解有关malloc的更多信息,您可以阅读其手册页 - man malloc

  2. 所有malloc'ed内存必须为free

  3. Difference between scanf() and fgets()C - scanf() vs gets() vs fgets()解释了为什么fgets()优先于scanf()

  4. 代码如下:

    #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()

的返回值