0

動的に割り当てられた構造体の 2D 配列を含むプロジェクトに取り組んでいます。これらの各構造体の内部には、動的に割り当てられた int の配列があります。プログラムは完全に実行されていますが、配列の 2 番目の次元のメンバーが複数ある場合にクリーンアップするときに、次のエラーが発生します。

*** glibc detected *** cache: double free or corruption (out): 0x0000000009f172f0 ***

それに続く長いバックトレースとメモリ マップがあり、それが役立つ場合は、喜んで提供します。割り当てと解放のコードは次のようになります。ブロック構造体の内容は次のとおりです。

typedef struct blockStruct {
  int valid;
  int tag;
  int dirty;
  int mru;
  int* data;
} block;

割り当て: blocksPerSet が 1 より大きい場合、割り当て解除は失敗します。

 /* Make cache */
  block** cache;
  cache = malloc(numberOfSets * sizeof(block*));
  for (i = 0; i < numberOfSets; i++) {
    cache[i] = malloc(blocksPerSet * sizeof(block));
  }
  int j = 0;
  for (i = 0; i < numberOfSets; i++) {
    for (j = 0; j < blocksPerSet; j++) {
      cache[i][j].valid = 0;
      cache[i][j].data = malloc(blockSizeInWords*sizeof(int));
      cache[i][j].mru = 0;
    }
  }

割り当て解除:

 for (i = 0; i < numberOfSets; i++) {
    for (j = 0; j < blocksPerSet; j++) {
      free(cache[i][j].data);
    }
    free(cache[i]);
  }
  free(cache);

前もって感謝します。

編集:問題を2つの機能の1つに絞り込みました。それらは saveToCache と loadToCache であり、機能的に非常に似ています。メイン関数は常に最初に loadToCache を呼び出し、次に saveToCache を呼び出したり、loadToCache を呼び出したりするループです。これら 2 つの呼び出しのいずれかをコメントアウトすると、どちらに関係なく、エラーは発生しません。

EDIT2: loadToCache を使用する前に saveToCache を使用した場合にのみエラーが発生することにも気付きました。

int saveToCache(block** cache, int blockSizeInWords, int numberOfSets,
            int blocksPerSet, stateType* statePtr, int address,
            int saveData)

  int setNumber = (address / blockSizeInWords) % numberOfSets;
  int targetTag = address / blockSizeInWords / numberOfSets;
  int offset = address % blockSizeInWords;
  int blockStart = address / blockSizeInWords * blockSizeInWords;
  int i = 0;
  /* If a hit is found, set MRU and return */
  for (i; i < blocksPerSet; i++) {
    if (cache[setNumber][i].valid == 1) {
      if (cache[setNumber][i].tag == targetTag) {
        cache[setNumber][i].mru = 1;
        cache[setNumber][i].data[offset] = saveData;
        cache[setNumber][i].dirty = 1;
        printAction(address, 1, processorToCache);
        return cache[setNumber][i].data[offset];
      }
    }
  }
  int j;
  /* Find out if there is an empty space.  If so, allocate and return */
  for (i = 0; i < blocksPerSet; i++) {
    if (cache[setNumber][i].valid == 0) {
      cache[setNumber][i].valid = 1;
 cache[setNumber][i].tag = targetTag;
      cache[setNumber][i].dirty = 1;
      cache[setNumber][i].mru = 1;
      for (j = 0; j < blockSizeInWords; j++) {
         cache[setNumber][i].data[j] = (*statePtr).mem[blockStart + j];
      }
      printAction(blockStart, blockSizeInWords, memoryToCache);
      cache[setNumber][i].data[offset] = saveData;
      printAction(address, 1, processorToCache);
      return cache[setNumber][i].data[offset];
    }
  }
  int allMRUSet = 1;
  /* Find LRU and replace */
  int evictedAddress;
  for (i = 0; i < blocksPerSet; i++) {
    /* Save back to memory if block is dirty */
    if (cache[setNumber][i].mru == 0) {
      evictedAddress = blockSizeInWords * (setNumber + cache[setNumber][i].tag
                                       * numberOfSets);
      if (cache[setNumber][i].dirty == 1) {
        for (j = 0; j < blockSizeInWords; j++)
          (*statePtr).mem[blockStart + j] = cache[setNumber][i].data[j];
         printAction(evictedAddress, 1, cacheToMemory);
      }
      else
        printAction(evictedAddress, 1, cacheToNowhere);
      cache[setNumber][i].valid = 1;
      cache[setNumber][i].tag = targetTag;
      cache[setNumber][i].dirty = 1;
      cache[setNumber][i].mru = 1;
      for (j = 0; j < blockSizeInWords; j++) {
        cache[setNumber][i].data[j] = (*statePtr).mem[blockStart + j];
      }
      printAction(blockStart, blockSizeInWords, memoryToCache);
      cache[setNumber][i].data[offset] = saveData;
      /* Check if all MRU blocks are set.  If yes, unset all. */
      for (j = 0; j < blocksPerSet; j++) {
        if (cache[setNumber][j].mru == 0)
          allMRUSet = 0;
      }
      if (allMRUSet == 1) {
        for (j = 0; j < blocksPerSet; j++) {
          cache[setNumber][j].mru = 0;
          }
        /* Re-set most recently used block */
        cache[setNumber][i].mru = 1;
      }
      printAction(address, 1, processorToCache);
      return cache[setNumber][i].data[offset];
    }
  }
  /* If we get this far, all MRU bits are set.  Un-set all of them. */
  for (i = 0; i < blocksPerSet; i++) {
    cache[setNumber][i].mru = 0;
  }
  /* Place data in item 0 of set and set MRU */
  /* Save back to memory if block is dirty */
  evictedAddress = blockSizeInWords * (setNumber + cache[setNumber][0].tag
                                           * numberOfSets);
  if (cache[setNumber][0].dirty == 1) {
        for (j = 0; j < blockSizeInWords; j++)
          (*statePtr).mem[blockStart + j] = cache[setNumber][0].data[j];
        printAction(evictedAddress, 1, cacheToMemory);
      }
  else
    printAction(evictedAddress, 1, cacheToNowhere);
  cache[setNumber][0].valid = 1;
  cache[setNumber][0].tag = targetTag;
  cache[setNumber][0].dirty = 1;
  cache[setNumber][0].mru = 1;
  for (i = 0; i < blockSizeInWords; i++) {
    cache[setNumber][0].data[i] = (*statePtr).mem[blockStart + j];
  }
  printAction(blockStart, blockSizeInWords, memoryToCache);
  cache[setNumber][0].data[offset] = saveData;
  printAction(address, 1, processorToCache);
  return cache[setNumber][0].data[offset];
}
4

2 に答える 2

0

一般に、優れた防御プログラミング手法は、有効なものを指していない場合にポインタを0に設定することです。割り当てられる前または解放された後。次に、それらを解放できると期待する場合は、最初にポインターがゼロ以外であることを確認できます。リリースビルドでは常に、またはassert()コンパイルをゼロに設定できるなどを使用した開発/デバッグのためだけです。

プログラムロジックが、解放する前にポインタがゼロ以外であるかどうかのチェックに依存しない場合は、最適化として、リリースビルドでオフにできるように、ゼロをマクロまたは定数変数チェックでラップできます。

例えば

#define DEBUG_FREE 1

assert(ptr);
free(ptr);
if (DEBUG_FREE)
  ptr = 0;
于 2012-04-22T02:43:18.913 に答える
-1

あなたが投稿したコードのエラーはすぐに私に飛びつきませんが、あなたは astateTypeが何であるかを示していないので、 to/from のコピーを調べることで何かが見つかるのではないかと思います(*statePtr).mem[]. malloc されたスペースの終わりを使い果たすと、スペースが解放されたときに実際に障害が発生する可能性があります。

とにかく、まだ問題が見つからない場合は、次の 3 つの簡単な方法があります。

  1. valgrindを使用します。インストールと使用が非常に迅速かつ簡単で、メモリリーク、二重解放などに最適です.
  2. 関数に assert を追加saveToCache()して、計算されたキャッシュへのインデックスが予想される範囲内に収まることを確認します。
  3. 上記で問題が見つからない場合は、この素晴らしい記事で説明されているように設定MALLOC_CHECK_=1または使用するなど、他のメモリデバッグツールを分解してください。libsafe

(多少 Linux 環境を想定していますが、上で書いたことの多くは他の環境にも当てはまります。)

于 2012-04-22T02:23:48.560 に答える