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 したものはすべて覚えておいてください。

char*s が指すためのスペースを割り当てる必要があります

C では、achar*は「char へのポインタ」を意味します。char*配列のようにポインターにインデックスを付けることができるため、文字列によく使用されます-たとえば、次のように仮定します。

 char *a = "Hello";

次に、 「 が指す char の後a[1]の最初のもの、この場合は; 」を意味します。chara'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);
    

    これは、型の変更に対して堅牢です。常に、10 個のポイントにfName十分なスペースを割り当てます。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()文字列に保持できる文字数だけを読み取るように要求してください。

fNameで 10 文字を割り当てると、scanf()読み取られる文字数が多すぎる可能性があることに注意してください。"%Ns"N は文字列の最大文字数 (末尾の null ターミネータのマイナス 1) を記述することで、scanf に制限を明示的に伝えることができます。したがって、10 文字を割り当てた場合は、次のように記述します。

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

malloc からの戻り値をキャストする必要はありません。

最後のポイント - malloc の戻り値を C にキャストする必要はないので、おそらく次のように記述します。

 contact =  malloc (sizeof(contactInfo));

free()あなたがmallocしたものをすべて覚えておいてください

あなたはすでにこれを行っているかもしれませんが、何かをするたびに、それが終わったらコードにmalloc()対応するものがあることを確認してください。free()これにより、メモリを元に戻すことができることがオペレーティング システムに通知されます。だから、もしあなたがどこかにいるなら

 contact =  malloc (sizeof(contactInfo));

後で、その連絡先を使い終わったら、次のようなものが必要になります。

 free(contact);

メモリリークを避けるため。

何かを解放するとすぐに、それ以上アクセスすることはできなくなります。そのため、連絡先内で文字列を malloc した場合は、最初にそれらを解放する必要があります。

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

無料について覚えておくべきことがいくつかあります。

  1. 何かを 2 回解放することはできません。これを回避するには、次のように記述することをお勧めします。

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

    このように記述すると、作成時にすべてのポインターを NULL に初期化する必要があります。ポインターを含む構造体を作成する場合、これを行う簡単な方法は、構造体を作成するcalloc()代わりにを使用することです。これは、常にゼロのメモリが返されるためです。malloc()calloc()

  2. プログラムが終了すると、すべてのメモリがオペレーティング システムに解放されます。これは、技術的free()には、プログラムの存続期間中にあるものを必要としないことを意味します。ただし、malloc したものはすべて解放する習慣を身につけることをお勧めします。そうしないと、ある日、それが重要であることを忘れてしまうからです。


さらなる改善

コメンターが別の回答で指摘しているように、マジックナンバー (コードにハードコーディングされた数字) を使用することは一般的に悪い習慣です。上記の例では、文字列のサイズとして「10」をプログラムにハードコーディングしました。ただし、次のようにすることをお勧めします。

#define FNAME_MAX_LENGTH 10

そして後で行きます:

malloc(sizeof(char) * FNAME_MAX_LENGTH);

これには、文字列のサイズを任意の場所で変更する必要がある場合に、1 か所で変更できるという利点があります。また、誤って 100 または 1 を 1 か所に入力して、重大で見つけにくいバグを引き起こす可能性を防ぎます。

もちろん、長さを取得したので、長さを指定する呼び出し#defineを更新する必要があります。scanf()ただし、scanf()長さ - 1 が必要なため、 を使用して長さを指定することはできません#define(少なくとも、読みやすい方法ではありません)。

fgets()したがって、指定された長さ -1 まで (または行末まで、どちらか早いほう) まで読み取るに興味があるかもしれません。次に、次のことができます。

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

scanf()呼び出しの代わりに。この変更を行うもう 1 つの正当な理由は、それがちょっとした苦痛にscanf()なる可能性があることです。

したがって、上記の要約に加えて、次のようになります。

  1. 文字列の長さに #define を使用すると、問題を回避し、後でコードを変更しやすくなります。
  2. fgets()は よりも使いやすく、文字列の長さにscanf()a を使用することとの互換性が高くなります。#define
于 2012-04-19T05:03:55.903 に答える
1

@AdamMihalcin からの提案に従って、参照として役立つことを願って、ほぼ完全なコードを提供しました。

注意点:

  1. の戻り値をmalloc()確認する必要があります。ヒープからメモリを取得するためmalloc()、十分なメモリがない場合malloc()は を返すことがありNULLます。あなたについてもっと知るにmallocは、そのマニュアルページを読むことができます -man malloc

  2. malloc'ed メモリはすべて'ed にする必要がありますfree

  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 に答える