0

我正在研究一个涉及动态分配的二维结构数组的项目。在这些结构中的每一个内部都是一个动态分配的整数数组。该程序执行得很好,但是当我的数组的第二维有多个成员时需要清理时,我收到此错误:

*** 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];
}
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 的空间确实会导致在释放空间时出现故障。

无论如何,如果您仍然没有发现问题,这里有三种简单的方法:

  1. 使用valgrind。它的安装和使用非常快速和容易,非常适合内存泄漏、双重释放等。
  2. 在函数中添加断言,例如saveToCache()确认您计算到缓存中的索引是否在预期范围内。
  3. 如果上面没有找到问题,请打破其他内存调试工具,如设置MALLOC_CHECK_=1或使用libsafe,如这篇好文章中所述。

(我在某种程度上假设了 Linux 环境,但我上面写的大部分内容都适用于其他环境。)

于 2012-04-22T02:23:48.560 回答