web-dev-qa-db-ja.com

Cの配列に文字列を入力する方法は?

ユーザーから入力(文字列)を取得して配列に格納しようとしましたが、このコードを実行すると、プログラムがすぐにクラッシュしました。

#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;
}
3
Top Serious

この領域には少し混乱があるようです。主な問題は、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

物事を調べ、他の回答を検討し、さらに質問がある場合はお知らせください。

2
David C. Rankin

この行の内容:

_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 */
_

Scanfを使用しない別のアプローチ

文字列を読み取るためのより適切な方法は、 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
_
2
RoadRunner

Wordをポインタの配列として宣言しています(char * Word [3];)。データを保存するにはメモリを割り当てる必要があります。値を割り当てる前に、 malloc または同様の関数を使用してメモリを割り当てます。

0
Nithin P
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
 }
0
A.S.H

はい、文字ポインタの配列を宣言するだけでは不十分なため、コードがクラッシュします。文字列を格納できるメモリを指すようにポインタを設定する必要があります。

例えば。

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);
0
Anders