iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 16
0
自我挑戰組

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

Days 16: 垃圾回收器系列:解決全域變數的問題、靜態變數

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

本系列的大綱 傳送門


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

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


▌前文提要:

釋放標記分配這三個部分已經初步實現。

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

並且測試完成,成功運行。

不過這不是終點,因為程式使用了全域變數
所以今天來繼續完善,解決全域變數的部分。


▌全域變數的問題:

一般而言都不會建議過度使用全域變數

  • 第一,對變數命名造成困難。
  • 第二,變數的生命週期太長浪費記憶體空間
  • 第三,封裝性不好,無法預測哪裏改變了變數的值,
    • 也不能夠對輸入的值作檢測。

綜合以上,程式會難以維護

當然也有好處,
就是不用像下面這樣麻煩的寫...。


▌如何解決?

習慣是一個函數加上靜態變數去儲存數值,以代替全域變數。

函數限制變數視野範圍(scope),
靜態變數 可分配固定的記憶體空間。

在這裏請大家回去看 Days 4: 重溫指標:靜態變數的地址、函數回傳地址
Days 6: 重溫指標:不使用回傳地址方式的函數(續.)

正正是函數加上靜態變數的方法,請務必先了解當中的地址概念。(很重要
下面就不從頭說了,看不懂就 = w = 呵呵


▌解決全域變數的問題:

首先,創建一個儲存用的函數 connect_address_pool

void connect_address_pool(void ****get_address_pool, void **get_index){

    /*用於儲存記憶體地址池的空間*/
	static void **address_pool = NULL;
	static int index = 0;  //address_pool 的索引

	*get_address_pool = &address_pool;
	*get_index = &index;
}

有兩個靜態變數: address_poolindex

由於 address_pool 是二維的,
輸入一個三維指標的地址,去取得二維指標的地址,返回調用它的程式,
使調用它的程式可以操作 address_pool

下面是實際應用在釋放標記分配這三個部分的情況:

▌釋放的部分:

void free_all_register_address(void) {

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

	/*取得記憶體地址池的資料*/
	connect_address_pool(&address_pool, &index);
    
	/*遍歷 address_pool 的空間*/
	for (int i = 0; i < *index; i++) {
		free((*address_pool)[i]);  //釋放曾經記錄過的記憶體地址的空間
		(*address_pool)[i] = NULL;
	}

    /*釋放用於紀錄的空間*/
	free(*address_pool);
	*address_pool = NULL;
}

▌標記的部分:

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

	/*取得記憶體地址池的資料*/
	connect_address_pool(&address_pool, &index);
    
    /*擴大、重新分配用於儲存記憶體地址的空間*/
    void **temp_ptr = NULL;  //中轉指標
	temp_ptr = (void**)realloc(*address_pool, (*index + 1) * sizeof(void*));
    
    /*對realloc分配記憶體的錯誤檢測*/ // ...省略......
    
    /*成功建立的空間分配給address_pool*/
	*address_pool = temp_ptr;  //取得中轉指標的地址
    
    /*註冊記憶體地址*/
	(*address_pool)[*index] = address;
	(*index)++;  //空間擴大、索引移位
    
}

▌分配的部分:

分配的部分不包含操作 address_pool, index 的部分,
所以沒有分別喔。


▌現在加入 connect_address_pool 函數後,只要利用:

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

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

程式的 *address_pool*index
就會等價靜態變數 address_poolindex

令程式連接了/可以操作 address_poolindex


▌目前我習慣解決全域變數的問題的方法就醬~


▌封裝性不夠,還可以加強,繼續完善。


上一篇
Days 15: 垃圾回收器系列:初步測試 ( 代碼 )
下一篇
Days 17: 垃圾回收器系列:更好的封裝性
系列文
30 Days 如何把 C 語言偽裝成高階語言 OWO /31

尚未有邦友留言

立即登入留言