1. 程式人生 > >動態儲存管理_free崩潰

動態儲存管理_free崩潰

一、 生存期

從變數值的存在時間(生存期)來觀察。有的變數在程式執行的整個過程都是存在的,而有的變數則是在呼叫其所在函式才臨時分配儲存單元,而在函式呼叫結束後該儲存單元就馬上釋放了,變數就不存在了。

1.1 靜態儲存方式

是指在程式執行期間由系統分配固定的儲存空間的方式。

1.2 動態儲存方式

是指在程式執行期間根據需要進行動態的分配儲存空間的方式。下面我們討論一下free因何崩潰。

二、 free崩潰

void __cdecl free(
    _Pre_maybenull_ _Post_invalid_ void* _Block
    );

本宣告來自VS2017, 從宣告我們可以看到,free函式的引數只有1個——無型別的指標。這就帶來一個問題:釋放申請的空間,不借助其他引數,作業系統如何知曉用完待釋放的空間的大小呢?既然函式傳參時不能體現,肯定就在別處解決了。需要留心邊界標誌法

——大概含義就是申請下來的空間還要有頭部、尾部:頭部用於記錄大小,尾部用於碎片整理過程(小的空閒碎片合併成大的空閒碎片)。free因何越界原因就很清楚了,極有可能是因為破壞了邊界資訊

2.1 上越界

#include<malloc.h>
#include<stdio.h>
#include<assert.h>

int main()
{
	int *p = (int*)malloc(10);	//申請10byte的空間
	assert(NULL != p);
	p++;
	free(p);    //p向上偏移進行解引用取出塊的大小時發生錯誤    段錯誤
    return 0;
}

2.2 下越界

#include<malloc.h>
#include<stdio.h>
#include<assert.h>

int main()
{
	int *p = (int*)malloc(10);	//申請10byte的空間
	assert(NULL != p);
	for (int i = 0; i < 10 + 1; ++i)    //故意設定成陣列訪問越界
	{
		p[i] = 1;
	}
	free(p);    //下越界
	return 0;
}

2.3 重複釋放同一塊空間

#include<malloc.h>
#include<stdio.h>
#include<assert.h>

int main()
{
	int *p = (int*)malloc(10);	//申請10byte的空間
	assert(NULL != p);
	free(p);
	free(p);    //第二次,屬於重複free
	return 0;
}

三、 realloc 

對於動態記憶體開闢這個行為:我們不會去討論生存期或者地址高低,前者不必要是因為動態開闢的記憶體會一直存在 until 主動free或者程式結束;後者是因虛擬地址空間和段頁機制的存在,先申請的記憶體不一定位於低地址,後申請的記憶體不一定位於高地址。

3.1 情況1

#include<malloc.h>
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

int main()
{
	int *p = (int*)malloc(10);	//申請10byte的空間
	assert(NULL != p);
	printf("%x\n", p);

	int *q = (int*)malloc(10);    //防止在p的尾部追加空間

	p = (int*)realloc(p, 200);	//原有空間釋放,申請200byte的空間
	printf("%x\n", p);
	
    free(p);
	return 0;
}

3.2 情況2 

#include<malloc.h>
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

int main()
{
	int *p = (int*)malloc(10);	//申請10byte的空間
	assert(NULL != p);
	printf("%x\n", p);

	int *q = (int*)malloc(10);	//防止在p尾部新增空間

	p = (int*)realloc(p, 6);	//原有空間釋放,申請6byte的空間
	
    p[8] = 100;    
    
	printf("%x\n", p);
	free(p);    //如果是新申請的空間,越界時將崩潰,但是最後驗證了並沒有崩潰
	return 0;
}

 

3.3 小結

void* __cdecl realloc(
    _Pre_maybenull_ _Post_invalid_ void*  _Block,
    _In_ _CRT_GUARDOVERFLOW        size_t _Size
    );

宣告來自VS2017,_Size指的是新空間的大小。策略是的:1 如果再分配的空間 ≤ 原有的空間,realloc實際上是一個空殼函式,什麼也不做,申請下來的空間還是原來的空間,但是這一過程對使用者透明。這一點在3.2節得到了驗證——並沒有執行記憶體緊縮,以空間換區時間,回收記憶體是一件很麻煩的事情;2 如果再分配的空間>原有空間,可能會在原有空間後面追加一塊空間;也可能是先釋放原有空間,然後新申請一塊空間。後者是更為常見的,因為無法確保原有空間後是否還有足夠的空間。