C:结构中的动态int数组 - 内存释放

时间:2012-04-21 23:02:01

标签: c arrays dynamic struct

我正在开发一个涉及动态分配的2D结构数组的项目。每个结构中都有一个动态分配的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];
}

2 个答案:

答案 0 :(得分:0)

一般来说,一个好的防御性编程技术是当它们没有指向任何有效的东西时将指针设置为0;在分配之前或在它们被释放之后。然后,当您希望能够释放它们时,您可以首先检查指针是否为非零,或者在发布版本中始终检查指针,或者仅用于使用例如开发/调试。 assert()可以设置为编译为空。

如果你的程序逻辑不依赖于检查指针在释放之前是非零的,你可以将归零包装在宏或常量变量中,以便你可以将它作为发布版本关闭,作为优化。

E.g。

#define DEBUG_FREE 1

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

答案 1 :(得分:-1)

您发布的代码中的错误没有立即跳出来,但是您没有显示stateType是什么,所以这让我想知道是否可以通过检查副本到/来自{ {1}}。在释放空间时,运行malloc'd空间的结束确实会导致失败。

无论如何,如果你还没有找到问题,这里有三种简单的方法:

  1. 使用valgrind。这真的很快&amp;易于安装和使用 非常适合内存泄漏,双重释放等。
  2. (*statePtr).mem[]等函数中添加断言,以确认计算出的缓存中的索引是否符合预期范围。
  3. 如果上述情况没有找到问题,请按照this nice article中的说明分解其他内存调试工具,例如设置saveToCache()或使用MALLOC_CHECK_=1
  4. (我在某种程度上假设了Linux环境,但我上面写的很多内容都适用于其他环境。)