0

Cプログラムでファイルを読み取り、その中のすべての単語を、コマンドライン引数を介して入力された単語と比較しています。しかし、クラッシュが発生し、何が問題なのか理解できません。このようなエラーを追跡するにはどうすればよいですか?私の場合、何が問題になっていますか?

私のコンパイラはclangです。コードは正常にコンパイルされます。実行すると、「セグメンテーション違反」と表示されます。

これがコードです。

#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    char* temp = argv[1];
    char* word = strcat(temp, "\n");

    char* c = "abc";
    FILE *input = fopen("/usr/share/dict/words", "r");

    while (strcmp(word, c))
    {
        char* duh = fgets(c, 20, input);
        printf("%s", duh); 
    }

    if (!strcmp (word, c))
    {
        printf("FOUND IT!\n");
        printf("%s\n%s", word, c);  
    }

    fclose(input);    
}
4

4 に答える 4

4

ここでの問題は、Cの文字列を別の言語(C ++やJavaなど)と同じように処理しようとしていることです。この言語では、任意の量のデータを簡単に追加または読み取ることができるサイズ変更可能なベクトルです。

C文字列ははるかに低いレベルです。それらは単に文字の配列(またはそのような配列へのポインター。配列はCの最初の要素へのポインターのように扱うことができます)であり、文字列は最初のnull文字までのその配列内のすべての文字として扱われます。これらの配列は固定サイズです。任意のサイズの文字列が必要な場合は、を使用して自分malloc()で割り当てるか、希望するサイズでスタックに割り当てる必要があります。

ここで少し紛らわしいのは、非標準タイプを使用していることですstring。コンテキストを考えると、それはあなたのから来ていると思いますcs50.h、そしてそれは単なるtypedefchar *です。;char *の代わりに実際に使用すると、おそらく混乱が少なくなります。stringtypedefを使用すると、実際に何が起こっているのかがわかりにくくなります。

最初の問題から始めましょう。

    string word = strcat(argv[1], "\n");

strcat()2番目の文字列を最初の文字列に追加します。これは、最初の文字列のnullターミネータから始まり、2番目の文字列のnullに達するまで、2番目の文字列の最初の文字に置き換えられます。これが機能するためには、最初の文字列を含むバッファに、2番目の文字列を収めるのに十分なスペースが必要です。そうでない場合は、他の任意のメモリを上書きする可能性があります。これにより、プログラムがクラッシュしたり、その他の予期しない動作が発生したりする可能性があります。

これがイラストです。argv[1]に単語が含まれていて、helloバッファにこれに必要なスペースが正確にあるとします。それが他のデータになった後。例として記入しましotherたが、実際にはそうではありませんが、何でもかまいません。重要な場合と重要でない場合があります。

+---+---+---+---+---+---+---+---+---+---+---+---+
| h | e | l | l | o | \0| o | t | h | e | r | \0|
+---+---+---+---+---+---+---+---+---+---+---+---+

ここで、strcat()を追加するために使用すると"\n"、次のようになります。

+---+---+---+---+---+---+---+---+---+---+---+---+
| h | e | l | l | o | \n| \0| t | h | e | r | \0|
+---+---+---+---+---+---+---+---+---+---+---+---+

other後のデータを上書きしたことがわかりますhello。これはあらゆる種類の問題を引き起こす可能性があります。これを修正するには、を新しい文字列にコピーする必要があります。このargv[1]文字列には、十分なスペースともう1つの文字があります(末尾のnullを忘れないでください)。を呼び出しstrlen()て文字列の長さを取得し、に1を追加\nし、末尾のnullに1を追加して、必要な長さを取得できます。

実際には、コマンドラインから入力した単語にaを追加するのではなく、入力単語からを削除するか、最後の文字()を除くすべてを比較するために使用する\nことをお勧めします。一般に、Cでは文字列の追加を避けるのが最善です。文字列を追加すると、メモリを割り当ててコピーする必要があり、間違いを犯しやすく、非効率になる可能性があるためです。高級言語は通常、詳細を処理し、文字列の追加を容易にしますが、それでも同様に非効率的です。\nstrncmp()\n

編集後、これを次のように変更しました。

    char* temp = argv[1];
    char* word = strcat(temp, "\n");

ただし、これには同じ問題があります。Achar *は文字配列へのポインタです。変数tempは、実際の値ではなく、ポインタをコピーしているだけです。それはまだ同じバッファを指しています。これがイラストです。私はデモンストレーションの目的でアドレスを作成しています。実際のマシンでは、これらの間にさらに多くのオブジェクトがありますが、デモンストレーションの目的にはこれで十分です。

+------------+---------+-------+
|    name    | address | value |
+------------+---------+-------+
| argv       |    1000 |  1004 |-------+
| argv[0]    |    1004 |  1008 | --+ <-+
| argv[1]    |    1006 |  1016 | --|---+
| argv[0][0] |    1008 |   'm' | <-+   |
| argv[0][1] |    1009 |   'y' |       |
| argv[0][2] |    1010 |   'p' |       |
| argv[0][3] |    1011 |   'r' |       |
| argv[0][4] |    1012 |   'o' |       |
| argv[0][5] |    1013 |   'g' |       |
| argv[0][6] |    1014 |     0 |       |
| argv[1][0] |    1016 |   'w' | <-+ <-+
| argv[1][1] |    1017 |   'o' |   |
| argv[1][2] |    1018 |   'r' |   |
| argv[1][3] |    1019 |   'd' |   |
| argv[1][4] |    1020 |     0 |   |
+------------+---------+-------+   |

tempこれで、変数を作成するときargv[1]に、新しいものにコピーするだけchar *です。

+------------+---------+-------+   | 
|    name    | address | value |   |
+------------+---------+-------+   |
| temp       |    1024 |  1016 | --+
+------------+---------+-------+

argv[1]ちなみに、1より大きいことを確認せずにアクセスしようとしないでくださいargc。誰かが引数を渡さない場合、それargv[1]自体はアクセスできません。

次の問題に移ります。

    string c = "abc";

    // ...

        char* duh = fgets(c, 20, input);

ここでは、静的文字列を参照しています"abc"。のように、文字通りソースに表示される文字列"abc"は、プログラムのメモリの特別な読み取り専用部分に入ります。私が言ったことを覚えておいてください。stringこれはただの言い方char *です。つまりc、実際には、メモリのこの読み取り専用セクションへの単なるポインタです。abcまた、テキストで指定した文字(4、文字列を終了するnull文字)を格納するのに十分なスペースしかありません。fgets()最初の引数として、読み取っている文字列を格納する場所を取り、2番目の引数としてそれが持つスペースの量を取ります。したがって、最大20バイトを、4つのスペースしかない読み取り専用バッファーに読み込もうとしています。

たとえば、次のように使用して、スタックで読み取るためのスペースを割り当てる必要があります。

char c[20];

または動的に、を使用してmalloc()

char *c = malloc(20);
于 2012-11-18T22:11:10.760 に答える
1

私が見る最初の問題はこれです:

string word = strcat(argv[1], "\n");

ここでは、バッファの最後に文字を追加しています。実行時環境によって割り当てられたバッファー。読み取り専用と見なす必要があります。

編集

コードを変更しても同じ効果があります。

char* temp = argv[1];

と同じバッファをtemp指していargv[1]ます。バッファに適切なサイズを割り当てて使用する必要があります。

char* temp = (char*)malloc(sizeof(char) * (strlen(argv[1]) + 2));

+2追加用\nと最後\0用です。あなたがこれをするより:

strcpy(temp, argv[1]);
strcat(temp,"\n");
于 2012-11-18T21:51:11.827 に答える
1

コードにはかなり欠陥があります。もう1つ:

char* duh = fgets(c, 20, input);

ここでは、charへのポインタを定義し、初期化せず(したがって、ランダムな値が含まれています)、ランダムデータが指すアドレスに最大20バイトを書き込みます。運が良ければ、現金を手に入れることができます。そうでない場合は、他の重要なデータを上書きします。幸い、現在使用されているシステムのほとんどでは、別のプログラムのアドレススペースにアクセスできないため、コードはそれ自体にのみ大混乱をもたらします。

問題の行は次のようになります。

#define BUFFERSIZE 1024
...
while (reasonable condition) {
    char *duh = malloc(BUFERSIZE);
    if (NULL == duh) { /* not enough memory - handle error, and exit */
    }
    duh = fgets(duh, BUFFERSIZE, input);
    if (NULL == duh) { /* handle error or EOF condition */
    } else { /* check that the line is read completely,
        i.e. including end-of-line mark,
        then do your stuff with the data */
    }
    free (duh);
}

もちろん、バッファを(ループの外で)一度だけ割り当てて再利用することができます。を使用#defineすると、最大バッファサイズを簡単に調整できます。

または、最近のシステムでは、を使用できますgetline()。これにより、適切なサイズのバッファーを割り当てることができます。ループの最後にいる必要があります。 free()

Linux / BSDを使用している場合は、man(eg man fgets)を使用して関数に関する情報を取得します。それ以外の場合は、インターネットまたはCに関する適切な本を使用してドキュメントを入手します。

于 2012-11-18T22:14:11.130 に答える
0

まず、私のCの知識は古いので、文字列が何であるかわかりません。いずれにせよ、それは便利ですが、ファイルの内容を読み取るための事前にゼロ化された優れたバッファーが必要というわけではありません。したがってword、ゼロにするか次のようなことをするかにかかわらず、最初に入力をゼロにします。

#define IN_BUF_LEN 120
char in_buf[IN_BUF_LEN] = {0};

ほとんどのテキスト行の長さが約80文字以下であると仮定すると、120文字が安全なサイズです。

strcmp次に、実際にファイルを読み取るのではなく、の値のループに基づいています。同じことを達成するかもしれませんが、私whileはファイルの終わりに到達することに基づいています。

duh最後に、返されるものを格納する場所ではなく、ポインタを宣言しましたfgets。それも問題です。したがって、上記duhと同様に宣言する必要がありますin_buf

argv[1]最後に、実行時ではなく、コンパイル時にの値を割り当てます。それがあなたが望むものをどこにもたらしているのかわかりません。ポインタとして宣言tempしてからそれに割り当てるargv[1]と、への別のポインタができますがargv[1]、実際にはの値をargv[1]ローカル変数にコピーしていません。使ってみませんargv[1]か?

于 2012-11-18T21:57:48.643 に答える