iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 18
0
自我挑戰組

30 Days 如何把 C 語言偽裝成高階語言 OWO /系列 第 19

Days 18: 垃圾回收器系列:更多的功能

▌第一次閱讀本系列的,可以先看:

本系列的大綱 傳送門


▌第一次閱讀垃圾回收器系列,可以先看:

垃圾回收器系列 開始第一篇 傳送門


▌提要:

到目前為止有三個主要函數。

void free_all_register_address(void);               // 釋放
void register_address(void *address);               // 標記
void new_1d(void **ptr, int length, int type_side); // 分配

分別負責 釋放標記分配 這三個部分。

它們可以構成最基礎需求,但這並不足夠。
我們需要加入更多的功能,去滿足更多的需求。


▌有什麼功能可加入/完善?

先想一下使用者(程式員)還有什麼需求。

  1. 手動釋放 —— free()
  2. 改變分配後的空間大小。 —— realloc()

▌手動釋放:

透過 new_1d 分配後,地址被記錄並在程式結束後自動釋放,
若果自行加入 free() ,被記錄地址依舊會在結束後再次被釋放,
觸發雙重釋放的問題。

所以我們需要在手動釋放時,在 address_pool 中刪除被記錄地址。
register_address 的相反行為 —— 註銷地址

▌以下註銷地址的實作:

void deregist_address(void *address) {

	/*用於取得記憶體地址池的資料*/
	void ***address_pool = NULL;
	int *index;

	/*取得記憶體地址池的資料*/
	connect_address_pool(&address_pool, &index);

	/*遍歷 address_pool 的空間*/
	for (int i = 0; i < *index; i++) {

		/*具體註銷工作*/
		if ((*address_pool)[i] == address) {  //尋找對應的記憶體地址
			(*address_pool)[i] = NULL;  //註銷註冊
			break;  //減少迴圈開支
		}
	}
}

如果之前的文章都有看的話,絕對會明白,不廢話了。


▌以下手動釋放的實作:

void early_free(void *address) {
	deregist_address(address);
	free(address);
}

代碼很短,就是 釋放註銷。(次序不影響)


▌改變分配後的空間大小:

以上同理,如果自行調用 realloc() 去改變空間大小,
返回的地址若果不相同,會同時造成 內存洩漏雙重釋放

洩漏了新地址,雙重釋放了舊地址。

這裏牽涉到 realloc() 的特性,
可參看 Days 9: 動態記憶體分配:malloc()、calloc()、realloc()、free(),內存洩露


▌以下實作:

void re_1d(void **ptr, int length, int type_side){

    /*重新分配記憶體*/
	void *temp_ptr = NULL; //中轉指標
    temp_ptr = realloc(*ptr, length * type_side);  
    
    /*對realloc分配記憶體的錯誤檢測*/
	if (temp_ptr == NULL) {  //內存不足,記憶體分配失敗
		/*錯誤處理*/
	}

	/*分配後地址不相同*/
	if (temp_ptr != *ptr) {
		deregist_address(*ptr);  //註銷註冊
		register_address(temp_ptr);  //重新登記
	}

	/*成功建立的空間分配給ptr*/
	*ptr = temp_ptr;  //取得中轉指標的地址
}

分配後地址不相同,就註銷 地址,註冊 地址。


▌用法:

int *ptr = NULL;
new_1d(&ptr, 5, sizeof(int));
re_1d(&ptr, 10, sizeof(int));
early_free(ptr);

▌明白的,自然會明白。

(`・ω・´)


上一篇
Days 17: 垃圾回收器系列:更好的封裝性
下一篇
Days 19: 垃圾回收器系列:鎖碎事項:細節優化
系列文
30 Days 如何把 C 語言偽裝成高階語言 OWO /31

尚未有邦友留言

立即登入留言