2011-02-08 15 views
3

これは宿題を指定していくつかの文字列を並べ替えるためのものです。 scanfでソートしたい文字列の数を入力し、その数値に基づいて配列を割り当ててから、fgetsで文字列を取得します。scanfとfgetsの問題

文字列の数がハードコードされていればすべてうまく動作しますが、ユーザがネジを決定できるようにscanfが追加されています。ここでは、コードです:それは、配列の先頭に空の文字列で、その結果、ループの最初の反復をスキップし

 
Input the number of strings that you'd like to sort: 3 
Input string: Input string: foo 
Input string: bar 

#include <assert.h> 
#include <stdio.h> 
#include <stdlib.h> 

#define LENGTH 20 // Maximum string length. 

int main(void) 
{ 
    int index, numStrings = 0; 
    char **stringArray; 
    printf("Input the number of strings that you'd like to sort: "); 
    assert(scanf("%d", &numStrings) == 1); 
    stringArray = (char **)malloc(numStrings * sizeof(char *)); 

    for (index = 0; index < numStrings; index++) 
    { 
     stringArray[index] = (char *)malloc(LENGTH * sizeof(char)); 
     assert(stringArray[index] != NULL); 
     printf("Input string: "); 
     assert(fgets(stringArray[index], LENGTH, stdin) != NULL); 
    } 

    // Sort strings, free allocated memory. 

    return 0; 
} 

そして、ここでは次のようにコンソールが見えるものです。私の質問は、それがなぜそれを行うのか、それをどうすれば解決できるのかということです。ここで


は、コンソールがscanfに渡されたフォーマット文字列"%d\n"に見えるものです:だから

 
Input the number of strings that you'd like to sort: 3 
foo 
Input string: Input string: bar 
Input string: baz 

、私がすることができ、入力文字列のすべてが、文字列の最初のプロンプト間違った場所にあります。 [ヒットされたボタンを入力したときから]

scanf("%d\n", &numStrings) 

それなしで、scanf関数は、残留改行文字を読み込みます:

+4

ユーザー入力を読み取る入力関数など、合理的に失敗する可能性がある関数に対して 'assert()'を実行しないでください。エラーを無視するよりも優れています。エラーをうまく処理するほど良くはありません。 –

+0

@Jonathanアドバイスをいただきありがとうございます。 – gdejohn

答えて

3

実際の答え(私の謙虚だが、まったく正しい意見:P)はscanfを使用しないでください。 fgetsを使用して最初の行(数字など)を読み取ってから、sscanfまたはstrtoulと入力して文字列を解析します。そうすれば、誰かが素敵なフォーマットでデータを入力していないときにエラーを処理できるようになり、scanfの堅牢な空白処理が不十分でハッキングする必要がなくなります。

また、長さが-4の配列がたくさんあると予想される場合を除き、サイズを格納するためにintを使用したことはありません。標準では、符号なしタイプsize_tを、オブジェクトサイズと配列インデックスを格納するのに十分な大きさの符号なしタイプとして指定しています。他のタイプを使用することは保証されません。

+0

サイズを格納するintを使用しないと言ったときに私のコードのどの部分を参照していますか? – gdejohn

+0

@Charlatan - 'numberOfStrings'特に、' malloc'に渡すもの、 'strlen'から受け取るもの、または' size_t'の配列をインデックスとして使うものです。 –

+0

だから、 'stdin'から' malloc'に渡すために、 'size_t'型のものを得ることをどうお勧めしますか? intをパースしてキャストしますか?私は答えたところで(atoiで使われていた)あなたの答えに行って、今はうまくいきます。 – gdejohn

6

あなたはscanf関数で\ nを置くことによって、\ nを壊しするscanf関数を指示する必要がありますループの最初の行

+1

Foo Bahの答えに追加するいくつかのコメント:あなたのコードの1つの問題は、 'assert'の中に副作用があってはならないということです。 'assert'の中のものはリリースモードでは実行されません。また、 'printf'の後ろに' fflush(stdout); 'を置いて、ユーザに入力を求める前にプロンプ​​トが出力されるようにすることもできます。 –

+0

あなたの文字列が長すぎない場合、fgetsは終端改行文字を含むことに注意してください。 –

+0

そして文字列が長すぎると、 'fgets()'は次の呼び出し時に読み込まれる行の残りの文字をそのまま残します。標準I/O機能の1つ。 –