簡體   English   中英

C:結構中的動態int數組-內存釋放

[英]C: Dynamic int array in a struct - memory deallocation

我正在一個涉及動態分配的二維結構數組的項目中。 在每個這些結構內部是一個動態分配的int數組。 該程序執行完美,但是當我需要清理數組第二維的一個成員時,就會出現以下錯誤:

*** 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);

提前致謝。

編輯:我將問題縮小為兩個功能之一。 它們是saveToCache和loadToCache,並且在功能上非常相似。 主要功能是一個循環,該循環將始終先調用loadToCache,然后再調用saveToCache或調用loadToCache。 如果我注釋掉這兩個調用中的一個,無論哪個,我都不會出錯。

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];
}

通常,一種好的防御性編程技術是在指針未指向任何有效值時將其設置為0。 在分配它們之前或在釋放它們之后。 然后,當您希望能夠釋放它們時,可以先檢查指針是否為非零,要么始終在發行版本中進行檢查,要么僅使用例如assert()進行開發/調試assert()可以將其設置為不編譯)。

如果您的程序邏輯不依賴於釋放前檢查指針是否為非零,則可以將歸零包裝在宏或常量變量檢查中,以便可以將其關閉以進行發行版本優化。

例如

#define DEBUG_FREE 1

assert(ptr);
free(ptr);
if (DEBUG_FREE)
  ptr = 0;

您發布的代碼中沒有錯誤會立即引起我的注意,但是您沒有顯示stateType是什么,因此讓我想知道是否可以通過檢查往返(*statePtr).mem[]的副本來找到某些東西。 耗盡malloc分配的空間的末尾確實會導致釋放空間時出現故障。

無論如何,如果您仍然沒有找到問題,可以通過以下三種簡單的方法進行:

  1. 使用valgrind 它非常容易安裝和使用,非常適合內存泄漏,兩次釋放等。
  2. 在諸如saveToCache()類的函數中添加斷言,以確認您計算出的進入緩存的索引符合預期范圍。
  3. 如果上面沒有發現問題,打出來的其他內存調試工具,如設置MALLOC_CHECK_=1或使用libsafe在描述這個漂亮的文章

(我在某種程度上假設Linux環境,但是我上面寫的大部分內容都適用於其他環境。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM