3

我对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 输入一个值时,它会给我一个分段错误。如果我尝试在 contact->fName 前面添加 & ,我也会收到错误消息。

代码有什么问题?

4

3 回答 3

13

首先,别担心 - 开始 C 的挫折是正常的 :)

既然您说您是初学者,我已经写了一个很长的答案,解释了您可能想要进行的其他一些改进。对不起,如果我涵盖了一些你已经知道的事情。这是一个摘要:

  1. 您需要为char*s 分配一些空间来指向(这是导致崩溃的原因)
  2. 确保检查 malloc 的返回值
  3. 确保要求scanf()只读取字符串中可以容纳的尽可能多的字符。
  4. 无需从 malloc 转换返回值。
  5. 记住free()你已经 malloc-ed 的任何东西。

您需要为char*s 分配一些空间来指向

在 C 中,achar*表示“指向 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()可能会读取太多字符。"%Ns"您可以通过写入其中 N 是字符串中的最大字符数(对于末尾的空终止符减去 1)来明确告诉 scanf 限制。所以,如果你分配了 10 个字符,那么你应该写:

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

无需从 malloc 转换返回值。

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

 contact =  malloc (sizeof(contactInfo));

记住free()你分配的任何东西

您可能已经这样做了,但是每次您做malloc()任何事情时,请确保free()在完成后您的代码中有对应的。这告诉操作系统它可以取回内存。所以,如果你有什么地方

 contact =  malloc (sizeof(contactInfo));

稍后,当您完成与该联系人的联系后,您将需要以下内容:

 free(contact);

以避免内存泄漏。

一旦你释放了一些东西,你就不能再访问它了。因此,如果您在联系人中分配了字符串,则必须首先释放它们:

 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()在程序的整个生命周期内都存在的东西。然而,我建议养成释放所有你分配的所有东西的习惯,因为否则你会忘记有一天它很重要。


进一步改进

正如评论者在另一个答案中指出的那样,使用幻数(代码中硬编码的数字)通常是不好的做法。在我上面给你的例子中,我把“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()比 更容易使用,并且与使用 a作为字符串长度scanf()更兼容。#define
于 2012-04-19T05:03:55.903 回答
1

根据@AdamMihalcin 的建议,我给出了一个几乎完整的代码,希望可以作为参考。

注意几点:

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

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

  3. scanf() 和 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;
}
于 2012-04-19T05:11:02.227 回答
0

您应该为结构中的所有内容分配内存char *

例如:

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

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

于 2012-04-19T04:18:37.900 回答