0

strncat標準ライブラリの関数に奇妙な動作が見string.hられます。何が起こっているのかを理解する助けが必要です。


私の問題の核心は、ファイルの行を末尾の改行ターミネータなしで文字列readLineとして返す目的で呼び出された、私が作成した関数にあります。char *その関数は次のようになります。

char * readLine(FILE * fp) {
    char * chunk = NULL;
    char * line = NULL;
    int count = 0;

    // iterate through chunks of a line until we reach the end (or an error)
    while (fgets((chunk = malloc(sizeof(char) * BUFFER_SIZE)), BUFFER_SIZE, fp) != NULL) {

        // realloc on a null pointer works like malloc  
        line = realloc(line, ++count * BUFFER_SIZE * sizeof(char));    

        printf("chunk's contents: %s\n", chunk);

        // does chunk contain the end of a line?
        if(strchr(chunk, '\n') == NULL) {   
            // concatenate string parts and continue loop
            strncat(line, chunk, strlen(chunk) + 1);        
            free(chunk);

        } else {
            // we want to return a \0 terminated string without the \n
            // expected position of \n in chunk is ({length of chunk}-1)
            chunk[(strlen(chunk) - 1)] = '\0';

            // concatenate string parts
            strncat(line, chunk, strlen(chunk) + 1);
            printf("readLine line:    %s\n", line);
            free(chunk);

            break;        
        }        
    }
    return line;
}

次のようなループでメインで呼び出します。

FILE * fp = NULL;

if ((fp = fopen(FILE_PATH, "r")) != NULL) {
    char * line = NULL;

    while ((line = readLine(fp)) != NULL) {
        printf("main line:        %s\n\n", line);
        free(line);
    }

    fclose(fp);
}

ここで、奇妙な動作が の私の定義に入り#define BUFFER_SIZE 1000ます。そのように設定すると、次の出力が得られます(これは私が望むものではありません):

chunk's contents: I am on line 1
readLine line:    I am on line 1
main line:        I am on line 1

chunk's contents: Over here I am on line 2
readLine line:    I am on line 1Over here I am on line 2
main line:        I am on line 1Over here I am on line 2

chunk's contents: Line 3 here
readLine line:    I am on line 1Over here I am on line 2Line 3 here
main line:        I am on line 1Over here I am on line 2Line 3 here

chunk's contents: Look out for 4
readLine line:    I am on line 1Over here I am on line 2Line 3 hereLook out for 4
main line:        I am on line 1Over here I am on line 2Line 3 hereLook out for 4

chunk's contents: Johnny 5 alive!
readLine line:    I am on line 1Over here I am on line 2Line 3 hereLook out for 4Johnny 5 alive!
main line:        I am on line 1Over here I am on line 2Line 3 hereLook out for 4Johnny 5 alive!

しかし、その定義を のようなものに変更すると#define BUFFER_SIZE 20、探している出力が得られます。

chunk's contents: I am on line 1

readLine line:    I am on line 1
main line:        I am on line 1

chunk's contents: Over here I am on l
chunk's contents: ine 2

readLine line:    Over here I am on line 2
main line:        Over here I am on line 2

chunk's contents: Line 3 here

readLine line:    Line 3 here
main line:        Line 3 here

chunk's contents: Look out for 4

readLine line:    Look out for 4
main line:        Look out for 4

chunk's contents: Johnny 5 alive!

readLine line:    Johnny 5 alive!
main line:        Johnny 5 alive!

私は問題をstrncat(line, chunk, strlen(chunk) + 1);ラインに絞り込んだと思います。myが十分に高いのに、先行lineする s が含まれている理由がわかりません。BUFFER_SIZE

4

3 に答える 3

4
line = realloc(line, ++count * BUFFER_SIZE * sizeof(char));

割り当てられたメモリを初期化しません。したがって、最初の呼び出しで以前の呼び出しで取得したメモリのチャンクが返さreallocreadLineた場合、不可能ではありませんが、古い内容がまだ残っている可能性があります。

とにかく、初期化されていないメモリではstrncat、割り当てられたメモリに 0 バイトが存在する必要がないため、最初のメモリは未定義の動作を呼び出す可能性があります。

lineループに入る前にバッファを確保0し、1 バイト目に a を書き込みます。

また、使用しないでください

line = realloc(line, ++count * BUFFER_SIZE * sizeof(char));

失敗するとrealloc、メモリ リークが発生します。そして、の戻り値を確認する必要がありますrealloc

char *temp = realloc(line, ++count * BUFFER_SIZE * sizeof(char));
if (temp == NULL) {
  // Oops
} else {
    line = temp;
}

通話に参加しないmallocでください。chunkfgets

while (fgets((chunk = malloc(sizeof(char) * BUFFER_SIZE)), BUFFER_SIZE, fp) != NULL)

失敗するとmalloc、未定義の動作も発生します。malloc電話する前に確認してfgets

while ((chunk = malloc(sizeof(char) * BUFFER_SIZE)) && fgets(chunk, BUFFER_SIZE, fp) != NULL)
于 2013-03-01T20:30:55.940 に答える
1

reallocただし、以前にバッファをゼロに設定して memset することはできます。

于 2013-03-01T20:34:12.993 に答える
1

あなたの問題はここにあります:

    line = realloc(line, ++count * BUFFER_SIZE * sizeof(char));    

のマニュアルページによるとrealloc

"realloc(3) does not guarantee that the additional memory is also
 zero-filled."

"If ptr is NULL, realloc() is identical to a call to malloc() for size bytes."

したがって、取得した新しいメモリはゼロ以外のバイトでいっぱいになる可能性があります。つまり、最初に呼び出されたとき、おそらく最初のバイトが 0 になることはありません。つまり、strncat がジャンクに追加されることを意味します。バイトが割り当てられています。

于 2013-03-01T20:35:09.607 に答える