iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 19
0
自我挑戰組

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

Days 19: 垃圾回收器系列:鎖碎事項:細節優化

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

本系列的大綱 傳送門


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

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


▌前文提要:

目前加入了更多的功能,函數擴加至六個:

void free_all_register_address(void);               // 釋放
void early_free(void *address);                     // 手動釋放

void register_address(void *address);               // 標記
void deregist_address(void *address);               // 註銷

void new_1d(void **ptr, int length, int type_side); // 分配
void re_1d(void **ptr, int length, int type_side)   // 重新分配

new_1d() 代替了 malloc()
re_1d() 代替了 realloc()
early_free() 代替了 free()

而這篇包括主要講述一些瑣碎但重要/有意義的完善,
一些細節優化。


▌錯誤檢測:

顯然,在 new_1d 以及 re_1d 中,
lengthtype_side 輸入的值需要被檢測,
數值不可小於或等於 0 ,所以在函數的前面部分加入:

if( length <= 0 || type_side <= 0){
    /*錯誤處理*/
}

具體的錯誤處理,請期待 例外處理系列
(如果挑戰/嘗試失敗我會刪除這句,換成日誌輸出)(有構思但未進行實作


▌釋放地址的次序優化:

free_all_register_address 中,釋放地址的次序,
遵從先入先出的模式。(i.e. 遍歷方式:0, 1, 2, 3... index - 1)

個人認為先入後出較具意義。(i.e. 遍歷方式:index - 1, index - 2 ... 0)
例如 創建動態二維陣列,需要以與分配相反的次序釋放

故此把:

for(int i = 0; i < index; i++){

換成

for(int i = index - 1; i >= 0; i--){

▌用巨集作微小的效率優化:

early_free 函數的結構很簡單,
只是 free(address) + deregist_address(address)

由於函數調用需要消耗一定的時間和空間,
early_free 改為巨集函數,可減少一層函數調用,提供微小的效率優化。

#define early_free(address) free(address); deregist_address(address);

可讀性或許下降。


▌用巨集微調函數的使用方式:

目前 new_1d()re_1d() 的使用方式,
malloc()realloc() 比較,有微小的差異。

new_1d()re_1d() 是輸入指標的地址
malloc()realloc() 是輸入指標的值空間

使用者不需要知道函數內部的細節,使用方法越簡單越好
我們可以用巨集隱藏起這些細節。

建立巨集函數:

#define new_1d(ptr, length) new_1d(&ptr, length, sizeof(*ptr))
#define re_1d(ptr, length) re_1d(&ptr, length, sizeof(*ptr))

連打 type_side, sizeof() 的步驟也省去。

注意這個巨集宣告必須放置在 原函數 以及 任何使用到原函數 的 函數 後。

假設 int *ptr = NULL; 已經宣告,
原本是這樣使用的:

new_1d(&ptr, 5, sizeof(int));

現在是這樣使用的:

new_1d(ptr, 5);

是不是簡單多了~


▌再次修改 free_all_register_address

忘記了有使用者調用 free_all_register_address 的可能性,
意味函數有被運行兩次以上的機會,
所以 index 需要重設為零。

這又意味需要的權限不再只是讀取,而是連結。
所以回復為連結的形式,並在最後加入 *index = 0;


另一解決方案是用巨集覆蓋函數

#define free_all_register_address() printf("欸欸,不要用這個函數哦。");

注意這個巨集宣告必須放置在 被覆蓋函數 以及 任何使用到該函數 的 函數 後。

令使用者不能調用 free_all_register_address



▌下一篇講述 記憶體管理 須面對的「記憶體碎片化」問題,以及內存池等概念。

▌然後就完結 垃圾回收器系列 了。


上一篇
Days 18: 垃圾回收器系列:更多的功能
下一篇
Days 20: 垃圾回收器系列:記憶體碎片化、內存池
系列文
30 Days 如何把 C 語言偽裝成高階語言 OWO /31

尚未有邦友留言

立即登入留言