0

USACOの問題を解決しています。この問題では、2 つの文字列を入力として取り、47 を法とする数値を計算する必要があります。値が同じ場合は GO を出力し、そうでない場合は STAY を出力する必要があります。最初の数値は、アルファベットの数値 ( A の場合は 1、Z の場合は同様に 26 ) の積をとることによって計算され、最終的な数値はモジュロを使用して計算されます。

私のプログラムはエラーなしでコンパイルされており、最初のケースも成功しています。しかし、問題は 2 番目のケースと、ファイルが追加される方法にあります。プログラムは次のとおりです。

#include<stdio.h>
#include<malloc.h>
#include<string.h>
#define MAX 6
main()
{
    int cal(char *ptr);
    int a,b;
    char *comet,*group;
    FILE *fptr;
    comet=malloc(6*sizeof(char));
    group=malloc(6*sizeof(char));
    scanf("%s",comet);
    a=cal(comet);
    scanf("%s",group);
    b=cal(group);
    fptr=fopen("ride.out","a+");                (1)
    //fptr=fopen("ride.txt","a+");              (2)
    if(a==b)
        fprintf(fptr,"GO\n");               (3)
        //printf("GO\n");                    (4)
    else
        fprintf(fptr,"STAY\n");              (5)
        //printf("STAY\n");                   (6)   
    fclose(fptr);
    return 0;
}
int cal(char *ptr)
{
    int c,prod=1,mod;
    while(*ptr)
        {
                c=(*ptr++)-'A'+1;
                prod=prod*c;
        }
    mod=prod%47;
    return mod;
}

出力:-

出力画像

最初のケースは、2 つの文字列のセットです:-

    コメット
    HVNGAT

2 番目のケースは、エラー通知自体に記載されています。

(2) からコメント記号を削除して (1) に配置すると、ファイルの内容を確認でき、採点システムが必要とするとおりに表示されるため、プログラムは正常に動作しています。(1)の実際のステートメントでは発生していません。行 (4) と (6) のコメントも問題ありませんが、行 (1) はそうではありません。私はこれを理解することができません。何か助けはありますか?

4

1 に答える 1

2

最初にいくつかのメモ:

  • main():適切なメインは次のいずれかです。

    int main(void)
    or
    int main(int argc, char *argv[])
    
  • を使用すると、それが返されるかどうか、別名失敗するmalloc()かどうかを常に確認する必要があります。NULL

  • 常にfree()malloc されたオブジェクト。
  • 誰もが独自のコーディング スタイルを持っています。Cコーディングに関しては、これが非常に貴重であることがわかりました. それを他の多くの人のベースとして使用します。構造化されたコードであるという点は、読み取り、デバッグ、デコードなどが非常に簡単です。

コードの詳細:

cal() の署名

最初の行でmainの署名を宣言しますcal()。これは機能しますが、おそらくそれを上mainに置くか、cal()関数全体を上に置くでしょうmain

最大長

#define MAX 6決して使用しない定義があります。最大 6 文字で文字列を読み取る場合は、末尾のゼロも考慮する必要があります。

cplusplus.com scanfから:

指定子 's': 任意の数の非空白文字。最初に見つかった空白文字で停止します。終了ヌル文字は、格納されたシーケンスの最後に自動的に追加されます。

したがって:

#define MAX_LEN_NAME    7
...
comet = malloc(sizeof(char) * MAX_LEN_NAME);

使い方を学ぶのは良いことなので、ここでこのようにするmalloc()ことは何も悪いことではありません。しかし、それはそれと同じくらい単純なので、おそらく使用したいと思うでしょう:

char comet[MAX_LEN_NAME] = {0};
char group[MAX_LEN_NAME] = {0};

代わりは。少なくとも:使用する場合は、成功を確認し、完了したらmalloc解放します。それ以外の場合は、静的配列を使用します。

より安全な scanf()

scanf()given"%s"は、ターゲット バッファのサイズで読み取りを停止しません。空白を読み取るまで、メモリ内の連続するアドレスへのデータの読み取りと書き込みを続けます。例えば:

/* data stream = "USACOeRRORbLAHbLAH NOP" */
comet = malloc(szieof(char) * 7);
scanf("%s", buf);

メモリでは、次のようになります。

Address (example)
0x00000f           comet[0]
0x000010           comet[1]
0x000011           comet[2]
0x000012           comet[3]
0x000013           comet[4]
0x000014           comet[5]
0x000015           comet[6]
0x000016           comet[7]
0x000017           /* Anything; Here e.g. group starts, or perhaps fptr */
0x000018           /* Anything; */
0x000019           /* Anything; */
...

そして、上記の提案されたストリーム/文字列を読み取るとき、 を読み取るのではなくUSACOe、の範囲を超えcometて読み取りを続けます。言い換えれば、(かもしれない) 他の変数などを上書きします。これはばかげているように聞こえるかもしれませんが、C は低水準言語であるため、これは知っておく必要があることの 1 つです。そして、あなたがより多くを学ぶにつれて、おそらくそれの力を愛することも学ぶでしょう:)comet

maximum lengthこれを防ぐには、たとえば+を使用して読み取りを制限できます[what to read]。例えば:

 scanf("%6[A-Z]", comet);
         | |      |
         | |      +------- Write to `comet`
         | +-------------- Read only A to Z
         +---------------- Read maximum 6 entities

入力データ

期待される結果、エラー、(N)コメントなどを読むと、入力ファイルと出力ファイルが必要なように思えます。

あなたのコードは現在、標準入力からのデータの読み取りに依存していますstdin。したがって、 も使用しますscanf()代わりにファイルから読み取る 必要があると思いますfscanf()

だから:次のようなもの:

FILE *fptr_in;
char *file_data = "ride.in";
int res;
...
if ((fptr_in = fopen(file_data, "r")) == NULL) {
    fprintf(stderr, "Unable to open %s for reading.\n", file_data);
    return 1; /* error code returned by program */
}
if ((res = fscanf(fptr_in, "%6[A-Z]%*[ \n]", comet)) != 1) {
    fprintf(stderr, "Read comet failed. Got %d.\n", res);
    return 2;
}

b = cal(comet);

if ((res = fscanf(fptr_in, "%6[A-Z]%*[ \n]", group)) != 1) {
    fprintf(stderr, "Read group failed. Got %d.\n", res);
    return 2;
}

...

cal() 関数

まずはネーミング。これが、最終的に複数のファイルと数千行のコードになるプロジェクトの始まりだったとします。という名前の関数はおそらくないでしょうcal()。関数に適切な名前を付ける方法を学びます。コーディングスタイルに関する上記のリンクは、いくつかのポイントを提供します。私見は、小さなプロジェクトでもこれを行います。大きいものから大きいものへの書き込みが楽になる良い練習になります。名前を付けcprod_mod_47()ます。

その場合、mod変数 (および c かもしれません) は不要です。代替案は次のとおりです。

int cprod_mod_47(char *str)
{
    int prod = 1;

    while (*str)
        prod *= *(str++) - 'A' + 1;

    return prod % 47;
}

いくつかのより一般的な提案

コンパイル時には、多くの警告およびエラー オプションを使用します。たとえば、gcc を使用している場合は次のように言います。

$ gcc -Wall -Wextra -pedantic -std=c89 -o my_prog my_prog.c

これは途方もない量の助けです。valgrindさらに、非常にgdb貴重なツールの使用です。

于 2013-02-02T16:28:15.883 に答える