-9

この動的な再割り当てをポータブルな方法で機能させようとしています。

私のプログラムは、ユーザーからのテキスト行を受け取り、それをバッファーに追加します。バッファー内のテキストの長さが 20 以上の場合、最初の 20 文字を削除し、それ以降のすべての文字をバッファーの先頭に移動します。

Linux では問題なく動作するこのコードがありますが、Windows で実行するとガベージが発生します。mallocのみを使用してこれをポータブルにする理由/方法を知っている人はいますか? IE は string.h(strcpy) str を使用していません... len 以外のもの。

c17 のみ - 壊れたスタックはありません (ポータブルではありません)。これが私のコードです。エラーなしで gcc 7.3、mingw 7.3 をコンパイルします。get と put をより安全な関数に置き換えましたが、それでもウィンドウにガベージが表示されます。これはフォーマットの問題だと思います...

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

void wbuff (message)
    char *message;
{
    FILE *f = fopen("file.txt", "w");
    fprintf(f, "%s", message);
    fclose(f);
}

char *rean (message)
    char *message;
{
    /* performs (write) on buffer, trims lefover, then restores */

    char buf[80] = "";
    puts("enter a line");
    gets(buf);

    int bln  =  strlen( buf );
    int mln  =  strlen( message );
    int nln  =  bln + mln;
    printf("new length %d\n", nln);

    message = realloc(message, nln);
    memmove(message + mln, buf, bln);

    /* MISTAKE IS HERE?! */
    if( nln >= 20 ) {
        int exl  = nln -20;                        // leftover length
        char *lo = realloc(NULL, exl);             // leftover placeholder
        memmove(lo, message+20, exl);              // copy leftover
        wbuff(message);                            // write clear buff
        message = realloc(NULL, nln);
        message = realloc(NULL, exl);              // resize buffer
        memmove(message, lo, exl);                 // restore leftover
    }

    return message;
}

void main (void)
{
    char *message = "";
    message = realloc(NULL, 0);
    while ( 1 == 1 ) {
        message = rean( message );
        puts(message);
    }
    return;
}
4

1 に答える 1

5

C では、文字列はヌル バイトで終了する一連の文字です。ここには、主にその事実を考慮していないことに関連する多くのオフバイワンエラーと、メモリリークがあります。

最初に設定messageしたときmain:

char *message = "";
message = realloc(NULL, 0);

messageどちらも NULL で、メモリの 0 バイトを指します。あなたが最初に電話reanするとき:

int mln  =  strlen( message );

NULL ポインターを逆参照して、割り当てられたメモリの末尾を超えて読み取ろうとしています。最初に少なくとも 1 バイトを割り当て、そのバイトを 0 に設定して、空の文字列を作成します。

char *message = realloc(NULL, 1);
message[0] = '\0';

その後、バッファをメッセージにコピーすると、次のようになります。

message = realloc(message, nln);
memmove(message + mln, buf, bln);

終端の null バイトに十分なスペースを割り当てておらず、コピーもしていないため、実際には文字列がありません。その後、印刷しようとすると、割り当てられたメモリの末尾を超えて読み取られますputsprintf追加の 1 バイトを割り当て、追加の 1 バイトをコピーする必要があります。

message = realloc(message, nln + 1);     // allocate 1 extra byte for the null terminator
memmove(message + mln, buf, bln + 1);    // copy 1 extra byte

20 文字を超えるものを再コピーすると、同様の問題が発生します。

 int exl  = nln -20;                        // leftover length
 char *lo = realloc(NULL, exl);             // leftover placeholder
 memmove(lo, message+20, exl);              // copy leftover
 wbuff(message);                            // write clear buff
 message = realloc(NULL, nln);
 message = realloc(NULL, exl);              // resize buffer
 memmove(message, lo, exl);                 // restore leftover
  • 行 2 ~ 3: 終端の null バイトにスペースを割り当てたり、loコピーしたりしません。
  • 5 行目:最初の引数として使用中に代入を渡すことによりmessage、最初のによって以前に保持されていたメモリをリークします。reallocmessageNULL
  • 行 6-7: 同じことを行うことで、行 5 で割り当てられたメモリをリークします。また、null バイトにスペースを割り当てたり、次の行にコピーしたりしません。

前と同様に、割り当てごとに 1 バイト余分に割り当て、ヌル ターミネータを考慮して 1 バイト余分に移動します。また、ブロックの最後で解放し、余分なfor をlo削除し、以前の値をto に渡して、メモリ リークを防ぎます。reallocmessagemessagerealloc

 int exl  = nln -20;                        
 char *lo = realloc(NULL, exl + 1);         // allocate 1 extra byte
 memmove(lo, message+20, exl + 1);          // copy 1 extra byte
 wbuff(message);                            
                                            // remove extra realloc
 message = realloc(message, exl + 1);       // pass in old message, allocate 1 extra
 memmove(message, lo, exl + 1);             // copy 1 extra byte
 free(lo);                                  // free leftover

割り当てられたメモリの最後を超えた読み取りと書き込みの問題はすべて、未定義の動作を引き起こします。これが、オペレーティング システムごとに異なる結果が表示される理由を説明しています。

適合コードに関する限り、fgets代わりに次を使用しますgets

 fgets(line, sizeof(line), stdin);

この関数は、スペースがあれば改行を含めるlineので、その場合は必ず削除してください。

またmain、 returnintと remove に変更します。これは、関数#include <malloc.h>のファミリが に存在するように定義されているためです。mallocstdlib.h

andの代わりにstrcpyandを使用していた場合、null 終端バイトのコピーを考慮する必要はありません。これらの関数がそれを行うからです。ただし、メモリを割り当てるときは、それを考慮する必要があります。また、 、、およびの間に競合はありません。これらはすべて標準の一部であり、適切に連携します。併用しても問題ありません。それらが適切に機能しない場合は、それらを正しく使用していません。strcatmemmovestrcpymallocrealloc

私の更新を適用した後、これを置き換えることができます:

memmove(message + mln, buf, bln + 1);

これとともに:

strcat(message, buf);

そしてこれを置き換えます:

 memmove(lo, message+20, exl + 1);              // copy leftover
 ...
 memmove(message, lo, exl + 1);                 // restore leftover

これとともに:

 strcpy(lo, message+20);
 ...
 strcpy(message, lo);

そして、それはまだ適切に機能し、準拠しています。

于 2018-11-30T17:26:15.153 に答える