最初にいくつかのメモ:
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
貴重なツールの使用です。