[英]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分配的空間的末尾確實會導致釋放空間時出現故障。
無論如何,如果您仍然沒有找到問題,可以通過以下三種簡單的方法進行:
saveToCache()
類的函數中添加斷言,以確認您計算出的進入緩存的索引符合預期范圍。 MALLOC_CHECK_=1
或使用libsafe
在描述這個漂亮的文章 。 (我在某種程度上假設Linux環境,但是我上面寫的大部分內容都適用於其他環境。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.