0

stringそのため、作成したタイプで機能する関数がいくつかあります。それらの 1 つは、動的に割り当てられた文字列を作成します。もう 1 つは、前述の文字列を受け取り、それを拡張します。そして最後のものは文字列を解放します。注: 関数名は変更されていますが、すべて私が独自に定義したものです。

string new = make("Hello, ");
adds(new, "everyone");
free(new);

上記のコードは機能します - コンパイルして正常に実行されます。以下のコードは機能しません - コンパイル、実行、そして

string new = make("Hello, ");
adds(new, "everyone!");
free(new);

コードの違いは、adds()関数がもう 1 文字 (a ) を追加していること!です。追加する文字に違いはありません。長さだけです。完全を期すために、次のコードは機能しません。

string new = make("Hello, ");
adds(new, "everyone");
adds(new, "!");
free(new);

奇妙なことに、別の関数addc()(文字列の代わりに 1 文字を追加する) を使用する次のコードは機能します。

string new = make("Hello, ");
adds(new, "everyone");
addc(new, '!');
free(new);

同じことを行う以下も機能します。

string new = make("Hello, everyone!");
free(new);

動作しないすべてのエラーは次のとおりです。

test(526) malloc: *** error for object 0x100130: double free
*** set a breakpoint in malloc_error_break to debug

(test私がこれを持っているプログラムの非常にわかりやすい名前です。)

関数の内部に関する限り、 mymake()は への呼び出しと へstrlen()の 2 つの呼び出しと へmalloc()の呼び出しmemcpy()、 myadds()は への呼び出し、 へstrlen()の呼び出しrealloc()、 への呼び出しmemcpy()、 myfree()は標準ライブラリへの 2 つの呼び出しfree()です。

なぜ私がこれを取得しているのか、または分解してデバッガーを使用する必要があるのでしょうか? adds()sではなく、特定の長さを超える es でのみ取得していaddc()ます。

関数のコードを分解して投稿する:

typedef struct _str {
  int _len;
  char *_str;
} *string;

string make(char *c)
{
    string s = malloc(sizeof(string));
    if(s == NULL) return NULL;
    s->_len = strlen(c);
    s->_str = malloc(s->_len + 1);
    if(s->_str == NULL) 
      {     
        free(s);
        return NULL;
      }
    memcpy(s->_str, c, s->_len);
    return s;
}

int adds(string s, char *c)
{
    int l = strlen(c);
    char *tmp;
    if(l <= 0) return -1;
    tmp = realloc(s->_str, s->_len + l + 1);
    if(!tmp) return 0;
    memcpy(s->_str + s->_len, c, l);
    s->_len += l;
    s->_str[s->_len] = 0;
    return s->_len;
}

void myfree(string s)
{
    if(s->_str) free(s->_str);
    free(s);
    s = NULL;
    return;
}
4

6 に答える 6

4

最初の malloc は次のようにmakeなります。

malloc (sizeof (struct _str));

そうしないと、へのポインターに十分なスペースしか割り当てられませんstruct _str

于 2009-03-16T01:19:45.590 に答える
4

私が修正する潜在的な問題の数:

1/make()文字列のヌルターミネータをコピーしていないため、危険です。

2/ これは渡されたパラメーターであり、渡された実際のパラメーターには影響しないため、inに設定sすることもほとんど意味がありません。NULLmyfree()

adds()3/追加された文字列の長さが 0 以下の場合に-1 を返す理由がわかりません。まず、マイナスにはなりません。第 2 に、空の文字列を追加できる可能性が非常に高く、その結果、文字列は変更されず、現在の文字列の長さが返されます。失敗した場合 (つまり、機能しなかった場合) にのみ長さ -1 を返し、それrealloc()が発生した場合に古い文字列が保持されるようにします。

tmp4/変数が変更される可能性があるにもかかわらず、変数を保存していませんs->_str-サイズを増やしている場合、メモリをその場で再割り当てすることはめったにありませんが、増加が十分に小さく、割り当てられた余分なスペースに収まる場合は可能ですmalloc(). malloc()の実装で異なるサイズのメモリ ブロックに異なるバッファ プールを使用しない限り、サイズを縮小すると、ほぼ確実にインプレースで再割り当てされます。ただし、このコードでメモリ使用量を削減することはないため、これは余談です。

5/ここでのあなたの特定の問題は、構造自体ではなく、構造へのポインターである文字列にのみスペースを割り当てていることだと思います。これは、文字列を入れると、メモリ アリーナが破損していることを意味します。

これは私が書いたであろうコードです (よりわかりやすい変数名を含みますが、それは私の好みです)。

私は変わった:

  • からの戻り値はadds()、長さとエラー条件をより適切に反映します。現在は、展開できなかった場合 (および元の文字列が変更されていない場合) にのみ -1 を返します。それ以外の戻り値は、新しい文字列の長さです。
  • myfree()" " のような文字列を NULL に設定したい場合の戻り値s = myfree (s)
  • 割り当てられたなしでは割り当てられないため、文字列をチェックインmyfree()します。NULLstringstring->strChars

ここにあります。必要に応じて使用してください (または使用しないでください :-):

/*================================*/
/* Structure for storing strings. */

typedef struct _string {
    int  strLen;     /* Length of string */
    char *strChars;  /* Pointer to null-terminated chars */
} *string;

/*=========================================*/
/* Make a string, based on a char pointer. */

string make (char *srcChars) {
    /* Get the structure memory. */

    string newStr = malloc (sizeof (struct _string));
    if (newStr == NULL)
        return NULL;

    /* Get the character array memory based on length, free the
       structure if this cannot be done. */

    newStr->strLen = strlen (srcChars);
    newStr->strChars = malloc (newStr->strLen + 1);
    if(newStr->strChars == NULL) {     
        free(newStr);
        return NULL;
    }

    /* Copy in string and return the address. */

    strcpy (newStr->strChars, srcChars);
    return newStr;
}

/*======================================================*/
/* Add a char pointer to the end of an existing string. */

int adds (string curStr, char *addChars) {
    char *tmpChars;

    /* If adding nothing, leave it alone and return current length. */

    int addLen = strlen (addChars);
    if (addLen == 0)
        return curStr->strLen;

    /* Allocate space for new string, return error if cannot be done,
       but leave current string alone in that case. */

    tmpChars = malloc (curStr->strLen + addLen + 1);
    if (tmpChars == NULL)
        return -1;

    /* Copy in old string, append new string. */

    strcpy (tmpChars, curStr->strChars);
    strcat (tmpChars, addChars);

    /* Free old string, use new string, adjust length. */

    free (curStr->strChars);
    curStr->strLen = strlen (tmpChars);
    curStr->strChars = tmpChars;

    /* Return new length. */

    return curStr->strLen;
}

/*================*/
/* Free a string. */

string myfree (string curStr) {
    /* Don't mess up if string is already NULL. */

    if (curStr != NULL) {
        /* Free chars and the string structure. */

        free (curStr->strChars);
        free (curStr);
    }

    /* Return NULL so user can store that in string, such as
       <s = myfree (s);> */

    return NULL;
}

私が見ることができる他の唯一の改善点は、スペースのバッファーと の終わりを維持して、strCharsを呼び出さずに拡張のレベルを許可することmalloc()です。

これには、バッファの長さと文字列の長さの両方が必要であり、文字列の長さと新しい文字の長さの組み合わせがバッファの長さよりも大きい場合にのみ、より多くのスペースを割り当てるようにコードを変更する必要があります。

これはすべて関数にカプセル化されるため、API はまったく変更されません。また、文字列のサイズを縮小する関数を提供する場合でも、メモリを再割り当てする必要はなく、バッファの使用量を減らすだけです。compress()その場合、大きなバッファーと小さな文字列を持つ文字列を減らすために、おそらく関数が必要になるでしょう。

于 2009-03-16T01:21:33.370 に答える
2
 tmp = realloc(s->_str, s->_len + l + 1);

realloc は、要求されたブロックへの新しいポインターを返すことができます。次のコード行を追加する必要があります。

 s->_str = tmp;

1 つのケースではクラッシュしないが、もう 1 文字追加するとクラッシュする理由は、メモリの割り当て方法にあると考えられます。おそらく、最小割り当てデルタ (この場合は 16) があります。したがって、hello に最初の 8 文字を割り当てると、実際には 16 文字が割り当てられます。全員を追加すると、16 文字を超えないため、元のブロックが返されます。ただし、17 文字の場合、realloc は新しいメモリ バッファーを返します。

add を次のように変更してみてください

 tmp = realloc(s->_str, s->_len + l + 1);
 if (!tmp) return 0;
 if (tmp != s->_str) {
     printf("Block moved!\n"); // for debugging
     s->_str = tmp;
 }
于 2009-03-16T01:51:33.127 に答える
1

関数では、再割り当てが必要なメモリブロックのアドレスは変更されないaddsと想定しています。realloc

tmp = realloc(s->_str, s->_len + l + 1);
if(!tmp) return 0;
memcpy(s->_str + s->_len, c, l);

これは小さな再割り当てには当てはまるかもしれませんが(通常、取得するメモリブロックのサイズは割り当てを最適化するために丸められるため)、これは一般には当てはまりません。reallocが新しいポインターを返す場合、プログラムは引き続き古いポインターを使用するため、問題が発生します。

memcpy(s->_str + s->_len, c, l);
于 2009-03-16T10:06:37.960 に答える
0

おそらくコードを投稿する必要がありますが、二重解放は、同じポインターで解放を 2 回呼び出していることを意味します。

  1. 末尾の \0 バイトの strlen に 1 を追加していますか?
  2. ポインターを解放したら、メンバー変数を NULL に設定して、再度解放しないようにしますか (または、0xFFFFFFFF のような既知の不良ポインターに)
于 2009-03-16T01:01:46.523 に答える
0

「私の free() は、標準ライブラリ free() への 2 つの呼び出しです」なぜあなたは無料で2回電話をかけているのですか?電話する必要があるのは 1 回だけです。

あなたの add(); を投稿してください。および free() 関数。

于 2009-03-16T01:07:02.767 に答える