ユーザーから入力(文字列)を取得して配列に格納しようとしましたが、このコードを実行すると、プログラムがすぐにクラッシュしました。
#include <stdio.h>
int main() {
int i;
char *Word[3];
for(i=0;i<3;i++)
{
printf(" Enter a Word: ");
scanf("%s", &Word[i]);
}
printf("%s ", Word[0]);
return 0;
}
この領域には少し混乱があるようです。主な問題は、char *Word[3];
で宣言した各ポインタのアドレスに各Wordを書き込もうとしていることです。 (言うまでもなく、各ポインタが指す場所にストレージが割り当てられていませんが、各ポインタのアドレスにを&Word[i]
で書き込もうとすると、そこに到達することはありません。ポインタ自体よりも)
scanf
を使用することはできますが、scanf
を使用してユーザー入力を取得すると、すべての新しいCプログラマーを悩ませることになる多くの落とし穴の1つにすぐに遭遇します(例:'\n'
が入力バッファーに残っている、文字列の空白の処理に失敗している、読み取り/書き込み文字数の制限に失敗している、読み取りまたは処理のEOFの検証に失敗しました、など...)
より良いアプローチは、単にfgets
を使用してから、fgets
が読み取り、文字列を格納するバッファにを含める'\n'
をトリミングすることです。簡単な例は次のとおりです。
#include <stdio.h>
#include <string.h>
#define NWDS 3 /* declare a constant for the maximum number of words */
int main (void) {
int i, n = 0;
char Word[NWDS][50] = { "" }; /* provide storage or allocate */
for (i = 0; i < NWDS; i++) { /* for a max of NWDS */
printf ("Enter Word : "); /* Prompt */
if (!fgets (Word[i], sizeof Word[i], stdin)) /* read/validate */
break; /* protect against EOF */
size_t len = strlen (Word[i]); /* get length */
if (Word[i][len-1] == '\n') /* check for trailing '\n' */
Word[i][--len] = 0; /* overwrite with nulbyte */
}
n = i; /* store number of words read */
putchar ('\n'); /* make it pretty */
for (i = 0; i < n; i++) /* output each Word read */
printf (" Word[%d] : %s\n", i, Word[i]);
#if (defined _WIN32 || defined _WIN64)
getchar(); /* keep terminal open until keypress if on windows */
#endif
return 0;
}
入力中にEOF
を生成することにより、いつでも入力をキャンセルできます(ctrl + d Linuxまたは ctrl + z ウィンドーズで)、あなたは覆われています。
使用例/出力例
$ ./bin/wordsread
Enter Word : first Word
Enter Word : next Word
Enter Word : last Word
Word[0] : first Word
Word[1] : next Word
Word[2] : last Word
物事を調べ、他の回答を検討し、さらに質問がある場合はお知らせください。
この行の内容:
_scanf("%s", &Word[i]);
_
_Word[i]
_がどこかを指していること、および入力された文字列を占有するのに十分なスペースがあることを確認する必要があります。 _Word[i]
_は_char *
_ポインタであるため、いつかこのためにメモリを割り当てる必要があります。それ以外の場合は、どこも指していないダングリングポインタです。
scanf()
を使い続けたい場合は、 malloc
を使用して事前にスペースを割り当てることができます。
malloc()
は、要求されたメモリをヒープに割り当て、最後に_void*
_ポインタを返します。
次のように、コードにmalloc()
を適用できます。
_size_t malloc_size = 100;
for (i = 0; i < 3; i++) {
Word[i] = malloc(malloc_size * sizeof(char)); /* allocates 100 bytes */
printf("Enter Word: ");
scanf("%99s", Word[i]); /* Use %99s to avoid overflow */
/* No need to include & address, since Word[i] is already a char* pointer */
}
_
注:失敗するとNULL
を返す可能性があるため、malloc()
の戻り値を確認する必要があります。
さらに、malloc()
を使用してメモリを割り当てる場合は常に、 free
を使用して、最後に要求されたメモリの割り当てを解除する必要があります。
_free(Word[i]);
Word[i] = NULL; /* safe to make sure pointer is no longer pointing anywhere */
_
文字列を読み取るためのより適切な方法は、 fgets
を使用することです。
char *fgets(char *str, int n, FILE *stream)
は、入力ストリームから行を読み取り、バイトを_char *str
_にコピーします。これには、占有できるスペースのしきい値としてn
バイトのサイズを指定する必要があります。
fgetsに関する注意事項:
\n
_文字を追加します。簡単に取り外せます。NULL
を返します。文字が読み取られない場合でも、最後にNULL
を返します。n
で静的に宣言する必要があります。stdin
または_FILE *
_のいずれかから。stdin
から入力行を読み取るために使用する方法の例を次に示します。
_char buffer[100]; /* statically declared buffer */
printf("Enter a string: ");
fgets(buffer, 100, stdin); /* read line of input into buffer. Needs error checking */
_
_#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUMSTR 3
#define BUFFSIZE 100
int main(void) {
char *words[NUMSTR];
char buffer[BUFFSIZE];
size_t i, count = 0, slen; /* can replace size_t with int if you prefer */
/* loops only for three input strings */
for (i = 0; i < NUMSTR; i++) {
/* read input of one string, with error checking */
printf("Enter a Word: ");
if (fgets(buffer, BUFFSIZE, stdin) == NULL) {
fprintf(stderr, "Error reading string into buffer.\n");
exit(EXIT_FAILURE);
}
/* removing newline from buffer, along with checking for overflow from buffer */
slen = strlen(buffer);
if (slen > 0) {
if (buffer[slen-1] == '\n') {
buffer[slen-1] = '\0';
} else {
printf("Exceeded buffer length of %d.\n", BUFFSIZE);
exit(EXIT_FAILURE);
}
}
/* checking if nothing was entered */
if (!*buffer) {
printf("No string entered.\n");
exit(EXIT_FAILURE);
}
/* allocate space for `words[i]` and null terminator */
words[count] = malloc(strlen(buffer)+1);
/* checking return of malloc, very good to do this */
if (!words[count]) {
printf("Cannot allocate memory for string.\n");
exit(EXIT_FAILURE);
}
/* if everything is fine, copy over into your array of pointers */
strcpy(words[count], buffer);
/* increment count, ready for next space in array */
count++;
}
/* reading input is finished, now time to print and free the strings */
printf("\nYour strings:\n");
for (i = 0; i < count; i++) {
printf("words[%zu] = %s\n", i, words[i]);
free(words[i]);
words[i] = NULL;
}
return 0;
}
_
入力例:
_Enter a Word: Hello
Enter a Word: World
Enter a Word: Woohoo
_
出力:
_Your strings:
words[0] = Hello
words[1] = World
words[2] = Woohoo
_
Wordをポインタの配列として宣言しています(char * Word [3];)。データを保存するにはメモリを割り当てる必要があります。値を割り当てる前に、 malloc または同様の関数を使用してメモリを割り当てます。
char *Word[3]; // <-- this is an array of 3 dangling pointers, of type char*
// they still point nowhere, we later need to set them to some allocated location.
...
for(i=0;i<3;i++) {
Word[i] = malloc(some_max_size * sizeof(char)); // <-- allocate space for your Word
printf(" Enter a Word: ");
scanf("%s", Word[i]); // <-- not &Word[i]; Word[i] is already a char* pointer
}
はい、文字ポインタの配列を宣言するだけでは不十分なため、コードがクラッシュします。文字列を格納できるメモリを指すようにポインタを設定する必要があります。
例えば。
const int maxLen = 32;
char* Word[3] = {NULL,NULL,NULL};
Word[i] = malloc(maxLen);
次に、キーボードから文字列を読み取り、文字列が長すぎないことを確認します。fgetsとmaxLenを使用します。
printf("Enter a Word:");
fgets(Word[i],maxLen,stdin);