2

ユーザーの入力で最も長い行を見つけて、行の長さと行自体を出力するCプログラムを書いています。文字のカウントには成功しますが、予期せず行自体の保存に失敗します。多分私はCのメモリ管理を誤解しており、誰かが私を修正することができます.

編集: フォローアップの質問: char に続くブロックdummyが割り当てられていないため、コンピューターがブロックを操作できる範囲が開いていることを理解しましたが、一部の char のストレージがまだ機能するのはなぜですか? 私が言及した 2 番目の例では、プログラムは文字を「未割り当て」ブロックに格納するべきではありませんが、格納します。なんで?

変数:

  • getchar()c毎回 iに保存されますgetchar()
  • i私がいる現在の行の長さ(これまでのところ)getchar()です
  • longest_iこれまでの最長の線の長さです
  • twostr2 つの文字列のうち最初の文字列の先頭を指します。最初の文字列は現在の行、2 番目の文字列はこれまでの最長の行です。行が最も長いことが検出されると、2 番目の文字列にコピーされます。将来の行がさらに長い場合、2 番目の文字列の一部が上書きされますが、もう使用しないので問題ありません。2 番目の文字列は、より右側の位置から開始されます。
  • dummytwostr指す場所を与える

これは、プログラムの変数によって使用されるメモリを視覚化する方法です。

 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|\n| 7|11|15|c |u |r |r |e |n |t |\0|e |s |t |\0|p |r |e |v |l |o |n |g |e |s |t |\0|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

真のステートメント:

&c == 11
&i == 12
&longest_i == 13
&twostr = 14
&dummy = 15

プログラム:

#include <stdio.h>

int main()
{
    char c = '\0';
    int i, longest_i;
    char *twostr;
    longest_i = i = 0;
    char dummy = '\0';
    twostr = &dummy;

    while ((c=getchar()) != EOF)
    {
        if (c != '\n')
        {
            *(twostr+i) = c;
            i++;
        }
        else
        {
            *(twostr+i) = '\0';
            if (i > longest_i)
            {
                longest_i = i;
                for (i=0; (c=*(twostr+i)) != '\0'; ++i)
                    *(twostr+longest_i+1+i) = c;
            }
            i = 0;
        }
    }

    printf("length is %d\n", longest_i);
    for (i=0; (c=*(twostr+longest_i+1+i)) != '\0'; ++i)
        putchar(c);

    return 0;
}

from *(twostr+longest_i+1))until'\0'は予測不能です。例:

入力:

longer line
line

出力:

length is 11
@

入力:

this is a line
this is a longer line
shorter line

出力:

length is 21
this is a longer lineÔÿ"
4

7 に答える 7

4

実際には、書き込み用のメモリを割り当てていません!

char dummy = '\0'; // creates a char variable and puts \0 into it
twostr = &dummy; // sets twostr to point to the address of dummy

この後は、ダミーによって確保された char の後に続くメモリに書き込み、誰が何を知っているかを上書きするだけです。

この場合の最も簡単な修正は、ダミーを char へのポインターにしてから、文字列に使用するバッファーを malloc することです (予想される最長の文字列よりも長くしてください!)。

たとえば、buffer以下は 256 バイト (ほとんどのシステムで) のメモリを指し、最大 255 文字の長さの文字列を許可します (末尾にヌル ターミネータ (\0) を格納するため)。

char * buffer = (char *)malloc(sizeof(char) * 256);

編集:これにより、ヒープからメモリが割り当てられます。これは、後で呼び出して解放する必要がありますfree(buffer);。別の方法は、Anders Kのソリューションに従って、スタック上のスペースを使い果たすことです。

于 2011-12-09T06:23:20.313 に答える
2

はい、あなたはCのメモリ管理モデルを誤解していると言っているのは正しいです。

行で

*(twostr+i) = c;

たとえば、twostr文字のアドレスが含まれていて、*twostr所有しているメモリのみを指しているという事実を除いて、これは正しいでしょう。0属するメモリのサイズdummyが1バイトであるため、別のアドレスを取得し、未定義の動作を生成する逆参照を除いて、それに何かを追加します。

したがって、長い話を短くするには、文字列を格納するためのメモリのチャンクを割り当てる必要があります。それを正しく行う方法を示すのが最も簡単なので、修正を加えたコードを次に示します。

#include <stdio.h>

int main()
{
    char c;
    int i, longest_i;
    char twostr[1024]; // twostr points to a block of memory 1024 bytes long
    char longest[1024]; // so does longest, where we will store the longest string

    longest_i = i = 0;
    char dummy = '\0';

    while ((c=getchar()) != EOF && i < 1024) // we check that i < 1024 so we don't
                                             // go outside the bounds of our arrays
    {
        if (c != '\n')
        {
            *(twostr+i) = c;
            i++;
        }
        else
        {
            twostr[i] = 0;
            if (i > longest_i)
            {
                longest_i = i;
                for (i = 0; twostr[i] != 0; ++i) { // 0 is the same as '\0'
                    longest[i] = twostr[i];
                    twostr[i] = 0; // fill twostr with NULLs
                }
            }
            i = 0;
        }
    }

    printf("length is %d\n", longest_i);
    for (i=0; longest[i] != 0; ++i)
        putchar(longest[i]);

    return 0;
}

さらに、プログラムの変数を視覚化する方法が正しくありません。それは本当に次のようなものになります:

スタック:

+---------+
|    c    |   1 byte
+---------+
|         |
|         |
|         |
|    i    |   4 bytes
+---------+
|         |
|         |
|         |
|longest_i|   4 bytes
+---------+
|         |
|         |
|         |

~~~~~~~~~~~

|         |
|         |
|  twostr |   1024 bytes
+---------+
|         |
|         |
|         |

~~~~~~~~~~~

|         |
|         |
| longest |   1024 bytes
+---------+
于 2011-12-09T06:32:30.773 に答える
2

によって読み取られた文字を格納するためのメモリが割り当てられていませんgetchar。あなたのポインターtwostrは、配列ではなく文字変数を指す文字ポインターですが、それをchar配列へのポインターとして扱っています:

char *twostr;
....
char dummy = '\0';
twostr = &dummy;
....
*(twostr+i) = c;  // when i here is > 0 you are accessing invalid memory.

必要なものは次のようなものです:

char *twostr = malloc(MAX);
// use it.
free(twostr);

whereMAXは、ユーザー入力の文字列の最大長よりも 1 大きいと定義されています。

于 2011-12-09T06:21:00.030 に答える
2

あなたはスタックを破壊しています。char ダミーには 1 バイトしか割り当てられていません。実際には次のようになります。

char ダミー[1024];

また、null ターミネータを許可するために、1024 または 1023 バイトを超えて書き込まないようにする必要があります。

于 2011-12-09T06:24:32.673 に答える
1

まず、管理している文字列を保持するのに十分なスペースがtwostrにあることを確認する必要があります。初期スペースを割り当てるため、および必要に応じて追加のスペースを割り当てるために、ロジックを追加する必要がある場合があります。何かのようなもの:

size_t twostrLen = 256;
char* twostr = malloc(twostrLen);

次に、これにデータを挿入します。インデックスが現在のtwostrLenの長さを超える場合は、追加のメモリを割り当てる必要があります。

if (i >= twostrLen) {
   char* tmp = twostr;
   twostrLen *= 2;
   twostr = malloc(twostrLen);
   memcpy(twostr, tmp, i-1);
   free(tmp);
}

書き込もうとしているものiからのオフセットはどこにありますか。twostr

最後に、現在の文字列から最長の文字列にコピーする場合、ループの終了条件はc=*(twostr+i)) != '\0'です。cこれは、一致したときにトリガーされ、終了nullが書き込まれる前'\0'にループを終了します。ループで文字列を出力するには、nullが書き込まれていることを確認する必要があります。最も内側のforループの後に以下を追加すると、問題に対処できるはずです。

*(twostr+longest_i+1+i) = 0;

これがないと、最後のループはヌル文字が検出されるまで読み取りを続けます。これはすぐに実行される場合もあれば(最初の例で機能しているように見える場合)、数バイト後の場合もあります(2番目の例のように追加の文字が出力されます)。

longest_i+1+i < twostrLen繰り返しますが、その場所に書き込む前にそれを確認することを忘れないでください。

于 2011-12-09T06:38:56.847 に答える
1

次のコードを試してください。期待どおりの結果が得られることを願っています。

#include <stdio.h>

#define LENGTH 1024

int main()
{
    char c;
    int i, longest_i;
    char twostr[LENGTH]=""; // twostr points to a block of memory 1024 bytes long
    char longest[LENGTH]=""; // so does longest, where we will store the longest string
longest_i = i = 0;
char dummy = '\0';

while ((c=getchar()) != EOF && i < LENGTH) // we check that i < 1024 so we don't
                                         // go outside the bounds of our arrays
{
    if (c != '\n')
    {
        *(twostr+i) = c;
        i++;
    }
    else
    {
        twostr[i] = 0;
        if (i > longest_i)
        {
            longest_i = i;
            for (i = 0; twostr[i] != 0; ++i) { // 0 is the same as '\0'
                longest[i] = twostr[i];
                twostr[i] = 0; // fill twostr with NULLs
            }
        }
        i = 0;
    }
}

printf("length is: %d\n", longest_i);
printf("And the word is: ");
puts(longest);
printf("\n");
return 0;
}
于 2011-12-09T07:24:07.197 に答える
1

twostr は文字を指しますが、バッファとして扱っています。

あなたがする必要があるのは、より多くの文字を保持できる代わりにバッファを作成することです

例えば

static char dummy[512];
twostr = dummy;
于 2011-12-09T06:21:09.530 に答える