2

ここの多くの人がstrcpyについて不満を言っていることは理解していますが、私が抱えている問題に対処する検索を使用したものは見つかりませんでした。

まず、strcpy自体を呼び出しても、クラッシュ/セグメンテーション違反自体は発生しません。次に、コードは関数内に含まれており、この関数を初めて呼び出すと、完全に機能します。クラッシュするのは2回目だけです。

私はLPC1788マイクロコントローラーでプログラミングしています。メモリはかなり限られているので、mallocのようなものが失敗する理由はわかりますが、解放されません。

関数trimMessage()にはコードが含まれており、関数の目的は、大きな文字列配列が大きくなりすぎた場合にその一部を削除することです。

void trimMessage()
{
  int trimIndex;
  // currMessage is a globally declared char array that has already been malloc'd
  // and written to.
  size_t msgSize = strlen(currMessage);

  // Iterate through the array and find the first newline character. Everything
  // from the start of the array to this character represents the oldest 'message'
  // in the array, to be got rid of.
  for(int i=0; i < msgSize; i++)
  {
    if(currMessage[i] == '\n')
    {
      trimIndex = i;
      break;
    }
  }
  // e.g.: "\fProgram started\r\nHow are you?\r".
  char *trimMessage = (char*)malloc((msgSize - trimIndex - 1) * sizeof(char));

  trimMessage[0] = '\f';

  // trimTimes = the number of times this function has been called and fully executed.
  // freeing memory just below is non-sensical, but it works without crashing.
  //if(trimTimes == 1) { printf("This was called!\n"); free(trimMessage); }
  strcpy(&trimMessage[1], &currMessage[trimIndex+1]);

  // The following line will cause the program to crash. 
  if(trimTimes == 1) free(trimMessage);
  printf("trimMessage: >%s<\n", trimMessage);

  // Frees up the memory allocated to currMessage from last iteration
  // before assigning new memory.
  free(currMessage);
  currMessage = malloc((msgSize - trimIndex + 1) * sizeof(char));

  for(int i=0; i < msgSize - trimIndex; i++)
  {
    currMessage[i] = trimMessage[i];
  }

  currMessage[msgSize - trimIndex] = '\0';
  free(trimMessage);
  trimMessage = NULL;

  messageCount--;
  trimTimes++;
}

助けてくれた皆さん、ありがとうございました。関数が正しく機能するようになりました。解放したばかりの配列を印刷しようとした理由を尋ねる人には、strcpyの後に問題が発生したことを示し、その後に続く他のコードを除外するためにそこにありました。

同様の問題に遭遇した人に役立つことがわかった場合に備えて、最終的なコードはここにあります。

void trimMessage()
{
  int trimIndex;
  size_t msgSize = strlen(currMessage);

  char *newline = strchr(currMessage, '\n'); 
  if (!newline) return;
  trimIndex = newline - currMessage;

  // e.g.: "\fProgram started\r\nHow are you?\r".
  char *trimMessage = malloc(msgSize - trimIndex + 1);

  trimMessage[0] = '\f';
  strcpy(&trimMessage[1], &currMessage[trimIndex+1]);

  trimMessage[msgSize - trimIndex] = '\0';

  // Frees up the memory allocated to currMessage from last iteration
  // before assigning new memory.
  free(currMessage);
  currMessage = malloc(msgSize - trimIndex + 1);

  for(int i=0; i < msgSize - trimIndex; i++)
  {
    currMessage[i] = trimMessage[i];
  }

  currMessage[msgSize - trimIndex] = '\0';
  free(trimMessage);

  messageCount--;
}
4

3 に答える 3

11

ヒープが破損している場合、またはヒープに無効なポインタを渡すと、freeはクラッシュする可能性があります。

それを見ると、最初のmallocは数バイト短いと思います。ヌルターミネータ用に1バイトを予約する必要があります。また、オフセット1にコピーするため、そのために別のバイトを予約する必要があります。したがって、コピーは次のヒープブロックの開始時に情報を上書きします(多くの場合、次のヒープブロックの長さと、それが使用されているかどうかの表示に使用されますが、これはRTLによって異なります) )。

次にフリーを行うときは、フリーブロックを合体させようとする可能性があります。残念ながら、次のブロックヘッダーが破損しているため、その時点で少し異常になります。

于 2012-08-31T14:49:01.697 に答える
2

コードのこれらの2行を比較します(もちろん、2行目を再配置しました)。

char *trimMessage = (char*)malloc((msgSize - trimIndex - 1) * sizeof(char));
      currMessage =        malloc((msgSize - trimIndex + 1) * sizeof(char));

キャストの不必要な違い(一貫性が重要です。2つのスタイルのどちらを使用するかはそれほど重要ではありませんが、同じコードで両方を使用しないでください)を除けば、長さには2バイトの違いがあります。2番目は、最初よりも正しい可能性が高くなります。

最初のケースでは2バイトの割り当てが少なすぎて、コピーmalloc()が依存するいくつかの制御情報を上書きしたため、free()管理するメモリが破損したためにクラッシュしました。

strcpy()この場合、問題は誤算ほどではありませんでした。

メモリの破損に関する問題の1つは、被害者のコード(問題を見つけるコード)が、問題の原因となったコードからかなり離れていることが多いことです。


このループ:

for(int i=0; i < msgSize; i++)
{
  if(currMessage[i] == '\n')
  {
    trimIndex = i;
    break;
  }
}

次のように置き換えることができます:

char *newline = strchr(currMessage, '\n');
if (newline == 0)
    ...deal with no newline in the current messages...
trimIndex = newline - currMessage;
于 2012-08-31T14:58:45.143 に答える
0

malloc()呼び出しの直前に次のコードを追加します。

// we need the destination buffer to be large enough for the '\f' character, plus
//      the remaining string, plus the null terminator
printf("Allocating: %d  Need: %d\n", (msgSize - trimIndex - 1), 1 + strlen(&currMessage[trimIndex+1]) + 1);

そして、私はそれがあなたに問題を示すと思います。

バッファサイズを手動で計算するとエラーが発生しやすくなることが何度も実証されています。それをしなければならない場合もありますが、エラーが発生しやすい側面を関数に処理させることができる場合もあります。

// e.g.: "\fProgram started\r\nHow are you?\r".
char *trimMessage = strdup( &currMessage[trimIndex]);

if (trimMessage && (trimMessage[0] == '\n')) {
    trimMessage[0] = '\f';
}

ランタイムにがない場合はstrdup()、実装するのは簡単です(http://snipplr.com/view/16919/strdup/)。

trimMessage()そして最後の編集として、これは私が同等であると信じている単純化された代替案です:

void trimMessage()
{
  char *newline = strchr(currMessage, '\n'); 
  if (!newline) return;

  memmove( currMessage, newline, strlen(newline) + 1);

  currMessage[0] = '\f';    // replace '\n'

  messageCount--;
}
于 2012-08-31T15:02:39.400 に答える