1

どこが間違っているのかわかりません。以下のコードは、オリンピック水泳選手の fname、lname country、および終了時間のユーザー入力を受け入れ、以下のように最速時間で結果を並べ替えることが期待されています。

**サンプル入力**
アドリントン レベッカ GBR 4:03.01
MUFFAT カミーユ FRA 4:01.45  
FRIIS ロッテ DNK 4:03.98
シュミット アリソン USA 4:01.77

**サンプル出力**
MUFFAT カミーユ FRA 4:01.45
シュミット アリソン USA 4:01.77
アドリントン レベッカ GBR 4:03.01
FRIIS ロッテ DNK 4:03.98
struct mtime_t {
    int mins;
    int secs;
    int fsecs;
};
typedef struct mtime_t mtime;

struct olympians { 
    char fname[15];
    char lname[15];
    char country[3];
    int time;
    int mins, secs, fsecs;
    mtime otime;
};

/* qsort struct comparision function (time float field) */ 
int struct_cmp_by_time(const void *a, const void *b) 
{ 
    struct olympians *ia = (struct olympians *)a;
    struct olympians *ib = (struct olympians *)b;
    return (int)(60*ia->time - 60*ib->time); 
}

/* struct array printing function */ 
void print_struct_array(struct olympians *array, size_t len) 
{ 
    size_t i;
    for(i=0; i<len; i++) 
        printf("%s %s %s \t %d:%d,%d\n", array[i].lname, array[i].fname, 
               array[i].country, &array[i].otime.mins, 
           &array[i].otime.secs, &array[i].otime.fsecs);

    puts("\n");
} 

/* sorting structs using qsort() */ 
void sort_structs_time() 
{ 
    int i, ath_num;

    struct olympians *ath_recs;
    scanf("%d", &ath_num);

    ath_recs = (struct olympians *) malloc(ath_num*sizeof(struct olympians));

    for(i = 0; i < ath_num; i++) {
        scanf("%s %s %s %d:%d,%d\n", ath_recs[i].fname, ath_recs[i].lname, 
              ath_recs[i].country, &ath_recs[i].mins, &ath_recs[i].secs, &ath_recs[i].fsecs);
    }

    puts("\n");

    /* print original struct array */ 
    print_struct_array(ath_recs, ath_num);

    /* sort array using qsort function */ 
    qsort(ath_recs, (size_t) ath_num, sizeof(struct olympians), struct_cmp_by_time);

    /* print sorted struct array */ 
    print_struct_array(ath_recs, ath_num);
}  

/* call to the function) */ 
int main() 
{ 
    /* run the function */
    sort_structs_time();
    return 0;
}
4

2 に答える 2

3

1 つの問題は次のとおりです。

char country[3];

GBRまたは 3 文字の文字列を保持するのに十分な大きさではありません。ヌル ターミネータには追加の文字が必要であり、 によって追加されscanf()ます。これにより、想定されていないメモリに書き込まれ、未定義の動作が発生します。への変更:

char country[4];

scanf()バッファ オーバーランを防ぐために読み取る文字数を制限し、scanf()期待されるすべての割り当てが行われたことを確認するために の戻り値をチェックすることをお勧めします。

if (6 == scanf("%14s %14s %3s %d:%d,%d\n", ...

4:03.01入力の時刻の形式は ですが、scanf()形式指定子,では最後の 2 つの を区切るために使用されることに注意してくださいint。への変更:

if (6 == scanf("%14s %14s %3s %d:%d.%d\n", ...
于 2012-08-29T14:18:27.277 に答える
1

もう1つの問題は、入力形式と出力形式,が小数点として使用されることです。サンプルデータは.小数点として使用します。

timeまた、もう1つの問題は、入力時にフィールドを値に設定しないことです。私はおそらく一緒に行きます:

for (i = 0; i < ath_num; i++)
{
    if (scanf("%s %s %s %d:%2d,%2d\n", ath_recs[i].fname, ath_recs[i].lname,
              ath_recs[i].country, &ath_recs[i].mins, &ath_recs[i].secs,
              &ath_recs[i].fsecs) != 6)
        break;
    ath_recs[i].time = (ath_recs[i].mins * 60 + ath_recs[i].secs) * 100 +
                       ath_recs[i].fsecs;
}

私が妄想的だった場合、分、秒、小数秒がすべてゼロまたは正(非負)であることを確認します。そして、おそらく、行全体をバッファに読み込んでから;fgets()で解析するコードを記述します。sscanf()エラー検出が容易になります。

char buffer[4096];
int i = 0;

while (fgets(buffer, sizeof(buffer), stdin))
{
    if (i >= ath_num)
        ...too much data...
    if (sscanf(buffer, "%s %s %s %d:%2d,%2d\n", ath_recs[i].fname, ath_recs[i].lname,
              ath_recs[i].country, &ath_recs[i].mins, &ath_recs[i].secs,
              &ath_recs[i].fsecs) != 6)
    {
         ...report problems...but I've got the whole line to identify which record
         ...is giving the problem — which helps you and the user...
         break;
    }
    ...other checking...
    ath_recs[i].time = (ath_recs[i].mins * 60 + ath_recs[i].secs) * 100 +
                       ath_recs[i].fsecs;
    i++;
}

...actual number of athletes is in i...

ユーザーにデータのカウントを行わせない方がよいでしょう。コンピューターは数えるのが得意です。移動しながら配列を割り当てる必要があります。これには、2次動作を回避するために少し注意が必要です。

次に、比較コードでは、60を掛ける必要はありません。

/* qsort struct comparision function (time float field) */ 
int struct_cmp_by_time(const void *a, const void *b) 
{ 
    const struct olympians *ia = (struct olympians *)a;
    const struct olympians *ib = (struct olympians *)b;
    if (ia->time > ib->time)
        return +1;
    else if (ia->time < ib->time)
        return -1;
    ...else if ... tie-breaker decisions - name? ...
    return 0; 
}

拡張可能であり、算術オーバーフローの問題を回避するため、コンパレータ関数に示されている構造を使用します。このアプリケーションでは、2つの時間の差が問題を引き起こす可能性はほとんどありませんが、問題を回避することは依然として良い考えであり、2つの整数を減算すると、一般にオーバーフロー(ラップアラウンド)が発生する可能性があります。


あなたのコードでは:

    printf("%s %s %s \t %d:%d,%d\n", array[i].lname, array[i].fname, 
           array[i].country, &array[i].otime.mins, 
       &array[i].otime.secs, &array[i].otime.fsecs);

私のコンパイラは、、、の&前にmins文句secsを言いfsecsます。コンパイラがそれを行っていない場合は、警告レベルを上げるまで上げるか、より優れたコンパイラを入手してください。

アスリートの印刷コードはのotimeサブ構造を使用してstruct olympiansいますが、コードはそれを設定していません(そして、別々のメンバー、、の値を複製しminsますsecsfsecs。それはあなたのコードをデバッグしている間私を少し軌道から外しました。このコードは機能します。これには、1人のアスリートを印刷するための別個の小さな関数print_athlete()(おそらく)が含まれており、コードはそれを使用します—しかし、入力データが出力に印刷されなかった理由を解明しながら、それを使用することもできました(まだコードにあります)。読み取った後に入力をエコーすることは、基本的なデバッグ手法です。コードは、成功したこともチェックします。(私は通常、次のような機能を持っていますprint_olympian()print_struct_array()malloc()void dump_olympian(FILE *fp, const char *tag, const struct olympian *athlete);複雑な構造を指定されたファイルに印刷します。タグも印刷されるので、dump関数への各呼び出しに注釈を付けることができます。ダンプ関数は通常、構造体のすべての要素をダンプする必要があります。)

本番コードには、関数のextern void err_error(const char *fmt, ...);ようなインターフェイスでエラーを報告するような一連の関数がありprintf()ます。err_error()も終了します。これにより、4行のエラー報告が1行に減ります。つまり、私がそれを行う可能性が高くなります。

.入力データの場合(との切り替えに注意,):

4
ADLINGTON Rebecca GBR 4:03,01 
MUFFAT Camille FRA 4:01,45  
FRIIS Lotte DNK 4:03,98 
SCHMITT Allison USA 4:01,77

出力は次のとおりです。

Processing 4 athletes
Rebecca ADLINGTON GBR    4:3,1
Camille MUFFAT FRA   4:1,45
Lotte FRIIS DNK      4:3,98
Allison SCHMITT USA      4:1,77


Rebecca ADLINGTON GBR    4:3,1
Camille MUFFAT FRA   4:1,45
Lotte FRIIS DNK      4:3,98
Allison SCHMITT USA      4:1,77


Camille MUFFAT FRA   4:1,45
Allison SCHMITT USA      4:1,77
Rebecca ADLINGTON GBR    4:3,1
Lotte FRIIS DNK      4:3,98

最初のブロックは、入力ループのデバッグ印刷からのものです。他の2つは、もちろん、元のコードの前後の画像です。


未修正の問題

解決する必要のある問題が1つあります(2つの部分で)。簡単なことは、出力は次のとおりです。

Rebecca ADLINGTON GBR    4:3,1

する必要があります

Rebecca ADLINGTON GBR    4:3,01

あるいは:

Rebecca ADLINGTON GBR    4:03,01

これは、印刷形式の%.2d代わりにを使用することで修正されます。%d

難しいのは、入力時間文字列が次の場合です。

4:03,1

4:03,01としてではなく、4:03,10として扱う必要があります。さらに悪いことに、4:3,9は4:03,09ではなく4:03,90である必要があります。これにより、後続のゼロが省略されたという理由だけで、ソートでアスリートに0.81秒の利点が与えられます(したがって、それは実際に重要です)。これには別の入力コードが必要になります。小数部分を長さ3の文字列に読み、%2[0-9]それを後処理して数値にすることもできます。これにより、1桁または2桁が入力されたかどうかを確認できます。

ちなみに、time構成部品を直接ソートすることで、完全に変換することを回避できます。そうすれば、コンパレータの体系的な構造が有益になります。

int struct_cmp_by_time(const void *a, const void *b) 
{ 
    const struct olympians *ia = (struct olympians *)a;
    const struct olympians *ib = (struct olympians *)b;
    int rc;
    if (ia->mins > ib->mins)
        return +1;
    else if (ia->mins < ib->mins)
        return -1;
    else if (ia->secs > ib->secs)
        return +1;
    else if (ia->secs < ib->secs)
        return -1;
    else if (ia->fsecs > ib->fsecs)
        return +1;
    else if (ia->fsecs < ib->fsecs)
        return -1;
    else if ((rc = strcmp(ia->lname, ib->lname)) < 0)
        return -1;
    else if (rc > 0)
        return +1;
    else if ((rc = strcmp(ia->fname, ib->fname)) < 0)
        return -1;
    else if (rc > 0)
        return +1;
    return 0; 
}

(全体として、あなたはコードを正しくすることにかなり近づいていました—よくできました。)

軽度にハッキングされたが機能するコード

推奨されるすべての変更がこのコードに含まれているわけではありませんが、多かれ少なかれ妥当な出力が生成されます。

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

struct mtime_t {
    int mins;
    int secs;
    int fsecs;
};
typedef struct mtime_t mtime;

struct olympians { 
    char fname[15];
    char lname[15];
    char country[4];
    int time;
    int mins, secs, fsecs;
    mtime otime;
};

/* qsort struct comparison function (time float field) */ 
int struct_cmp_by_time(const void *a, const void *b) 
{ 
    struct olympians *ia = (struct olympians *)a;
    struct olympians *ib = (struct olympians *)b;
    return (int)(60*ia->time - 60*ib->time); 
}

static void print_athlete(const struct olympians *ath)
{
    printf("%s %s %s \t %d:%d,%d\n", ath->lname, ath->fname, ath->country,
            ath->mins, ath->secs, ath->fsecs);
}

/* struct array printing function */ 
void print_struct_array(struct olympians *array, size_t len) 
{ 
    size_t i;
    for(i=0; i<len; i++) 
        print_athlete(&array[i]);
    puts("\n");
} 

/* sorting structs using qsort() */ 
void sort_structs_time(void) 
{ 
    int i, ath_num;

    struct olympians *ath_recs;
    scanf("%d", &ath_num);
    printf("Processing %d athletes\n", ath_num);

    ath_recs = (struct olympians *) malloc(ath_num*sizeof(struct olympians));
    if (ath_recs == 0)
    {
        fprintf(stderr, "Out of memory\n");
        exit(1);
    }

    for(i = 0; i < ath_num; i++) {
        if (scanf("%14s %14s %3s %d:%2d,%2d\n", ath_recs[i].fname, ath_recs[i].lname, 
              ath_recs[i].country, &ath_recs[i].mins, &ath_recs[i].secs, &ath_recs[i].fsecs) != 6)
        {
            fprintf(stderr, "Ooops\n");
            exit(1);
        }
        ath_recs[i].time = (ath_recs[i].mins * 60 + ath_recs[i].secs) * 100 +
                               ath_recs[i].fsecs;
        print_athlete(&ath_recs[i]);
    }

    puts("\n");

    /* print original struct array */ 
    print_struct_array(ath_recs, ath_num);

    /* sort array using qsort function */ 
    qsort(ath_recs, (size_t) ath_num, sizeof(struct olympians), struct_cmp_by_time);

    /* print sorted struct array */ 
    print_struct_array(ath_recs, ath_num);
}  

/* call to the function) */ 
int main(void) 
{ 
    /* run the function */
    sort_structs_time();
    return 0;
}
于 2012-08-29T14:46:21.750 に答える