まず、心配しないでください - C を始めるとイライラするのは普通のことです :)
あなたは初心者だと言うので、私はあなたがしたいと思うかもしれないいくつかの他の改善を説明するかなり長い答えを書きました. すでに知っていることをいくつかカバーして申し訳ありません。要約は次のとおりです。
char*
s が指すスペースを確保する必要があります(これがクラッシュの原因です)。
- malloc からの戻り値を確認してください
scanf()
文字列に保持できる文字数だけを読み取るように依頼してください。
- malloc からの戻り値をキャストする必要はありません。
free()
malloc したものはすべて覚えておいてください。
char*
s が指すためのスペースを割り当てる必要があります
C では、achar*
は「char へのポインタ」を意味します。char*
配列のようにポインターにインデックスを付けることができるため、文字列によく使用されます-たとえば、次のように仮定します。
char *a = "Hello";
次に、 「 が指す char の後a[1]
の最初のもの、この場合は; 」を意味します。char
a
'e'
あなたはこのコードを持っています:
contactInfo *contact;
contact = (contactInfo *) malloc (sizeof(contactInfo));
この時点で、contactInfo 構造体へのポインターを宣言し、それに適切なサイズのメモリを割り当てました。ただし、現在、構造内のポインターは何も指していないため、プログラムは を呼び出すとクラッシュしますscanf()
。読み込もうとしている文字にスペースを割り当てる必要もあります。次に例を示します。
contact->fName = malloc(sizeof(char) * 10);
10 文字分のスペースを割り当てます。構造体のすべてに対してこれを行う必要がありますchar*
。
あまり心配してほしくないいくつかの余談:
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);
無料について覚えておくべきことがいくつかあります。
何かを 2 回解放することはできません。これを回避するには、次のように記述することをお勧めします。
if(contact != NULL) free(contact);
contact = NULL;
このように記述すると、作成時にすべてのポインターを NULL に初期化する必要があります。ポインターを含む構造体を作成する場合、これを行う簡単な方法は、構造体を作成するcalloc()
代わりにを使用することです。これは、常にゼロのメモリが返されるためです。malloc()
calloc()
プログラムが終了すると、すべてのメモリがオペレーティング システムに解放されます。これは、技術的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()
なる可能性があることです。
したがって、上記の要約に加えて、次のようになります。
- 文字列の長さに #define を使用すると、問題を回避し、後でコードを変更しやすくなります。
fgets()
は よりも使いやすく、文字列の長さにscanf()
a を使用することとの互換性が高くなります。#define