memset 结构体内指针_数据结构之线性表应用——内存管理
大家好,我是隔壁小王,前面我給大家講了數據結構中的線性表,因為是第一次寫作,可能很多地方描述得不清楚,真是慚愧至極啊!這兩天我花了不少時間來學習如何寫作,也看了不少公眾號文章,今天我來講講線性表的應用,加深大家對線性表的應用。
不知道大家在電腦卡頓時會不會有這樣的情況,突然變得很卡,一般解決辦法就是關閉一些不必要的軟件或者更換更大的內存條來改善。關閉軟件我們不討論,我們來討論下這個內存。大家有沒有想過內存插上之后,OS(Operatin System)是怎么管理內存的,要知道,內存條就是一個大倉庫,要是沒有倉庫管理人員,以及管理方法倉庫會有多么的混亂。
由此引出我們今天的主題——“內存管理”,什么是內存管理,即內存管理是指軟件運行時對計算機內存資源的分配和使用的技術。內存管理有很多方法,我們討論比較簡單的也適合移植到單片機系統上的分塊式內存管理。
不難發現,分塊式內存管理由兩部分組成,內存池——儲存數據的地方,內存池被分成若干個塊。內存管理表——用于管理內存的分配釋放,管理表也被分成若干項,每一項指像分好塊的內存塊的首地址,講到這里,有沒有發現和我們上次講的線性表很相識啦,內存塊就是線性表中的數據項,管理表就是線性表中的索引,唯一的區別就在于內存管理的數據是指針,指向內存池。我們通過管理表來訪問內存池。
假設內存池大小為32Byte,現在我們需要65KiloByte的內存,首先我們用65 / 32 = 2余1,需要兩塊內存,但是余數也是需要內存的,所以還需要多分配一塊內存,所以總共需要3塊內存,然后算法會從末尾往前面找3塊連續的內存出來,接下來,是重要的一步,總得讓其他應用知道,這3塊內存是我的,誰也不能搶對吧!
所以我們要對這3塊內存對應的管理表做標記,并且每一塊都要標記
所以,就變成了下面這個亞子
這樣,應用就分配到了3塊內存,如果這個應用不用了,要釋放內存怎么辦,簡單!!直接修改管理表就可以了,真是渣男!在內存池上留下這么多足跡,結果改一下管理表就不管不顧了!
現在原理基本講完了,大家應該很清楚了吧!下面就是code time!(代碼時間)
#define MEM_BLOCK_SIZE 32 //內存塊大小(Byte)#define MEM_MAX_SIZE 1 * 1024 //內存池大小(KiloByte)#define MEM_TABLE_SIZE MEM_MAX_SIZE / MEM_BLOCK_SIZE //內存管理表大小(Byte)static uint8_t membase[MEM_MAX_SIZE];static uint16_t memmap[MEM_TABLE_SIZE];typedef struct {void (*init)(void); //內存初始化uint32_t (*perused)(void); //內存使用量uint8_t* base; //內存池uint16_t* map; //內存管理表uint8_t ready; //內存就緒}malloc_t;先是對定義內存池的相關參數,為了方便后面觀察,這里我們管理1kb的內存,大家不要小看這1kb,在單片機系統里面1kb能儲存1024b數據呢,哈哈哈!
我們定義了一個內存池和內存管理表,并且定義了內存管理的結構體。
void mem_init(void); void Memset(void* dst, uint8_t c, uint32_t size); void* Memcpy(void* dst, void* src, uint32_t size); uint8_t mem_free(uint32_t ptr); uint32_t get_mem_perused(void); uint32_t mem_malloc(uint32_t count);void Free(void* ptr); void* Malloc(uint32_t size); void* Realloc(void* ptr, uint32_t size)然后是相關的函數,其實用戶可以用到的就只有3個Malloc,Free,Realloc。
在使用內存之前我們要先初始化內存池和管理表
void mem_init(void) { Memset(membase, 0, MEM_MAX_SIZE); Memset(memmap, 0, MEM_TABLE_SIZE * 2); malloc_dev.ready = 1;}這樣,內存池被打掃得干干凈凈,管理表也準備開始工作
當我們調用Malloc的時候,會放生什么呢?
程序會去訪問內存管理表,并且找出連續可用的內存塊出來,這里我并沒有直接返回對應內存池的地址,而是返回一個相對地址偏移量,這是為什么呢?
因為,這個偏移量是相對于地址0x00000000開始的,但是我們的內存是的起始地址可能不是0x00000000,有可能是0x05201314,所以真實地址還得加上內存池的起始地址,從程序的模塊化來說,我把這兩個操作分開了。
uint32_t mem_malloc(uint32_t size){ uint32_t offset; //地址偏移量 uint32_t mem_block; //需要的內存塊數 uint32_t mem_find = 0,i; //以找到的內存塊數 if (size == 0) return 0xFFFFFFFF; mem_block = size / MEM_BLOCK_SIZE; if (size % MEM_BLOCK_SIZE) mem_block++; for (offset = MEM_BLOCK_SIZE - 1; offset > 0; offset--) { if(!malloc_dev.map[offset]) mem_find++; else mem_find = 0; if (mem_find == mem_block){ for (i = 0; i < mem_find; i++){ malloc_dev.map[offset + i] = (uint16_t)mem_find; //標記內存 } return offset * MEM_BLOCK_SIZE; }}return 0xFFFFFFFF;}當我們獲取到了相對偏移地址之后,咱們加上起始地址,是不是就得到了對應在內存池中的地址啦?很簡單吧!
void* Malloc(uint32_t size) { uint32_t offset; offset = mem_malloc(size); if (offset == 0xFFFFFFFF) return NULL; return (void*)(malloc_dev.base + offset);}申請內存是標記內存管理表,那釋放內存不就是申請的逆過程嘛,所以我們只要找到需要釋放的內存對應的管理表,將標記取消,拍拍屁股走人,就對了,因為沒有清空內存池中的數據,所以,大家在使用指針申請內存的時候有沒有發現,如果打印的話,會有隨機值,雖然在一開始我們初始化了內存池,它特別干凈,但是經過一段時間得運行,應用A使用一點,應用B也使用一點,釋放的時候也沒有去清空內存池,導致內存池中的數據是隨機的,這就是上一個應用不負責任的后果,讓下一個應用當老實人,要是不注意的話,這盤,哼哼哼哼。。。。
uint8_t mem_free(uint32_t ptr) { uint32_t i; if (!malloc_dev.ready) { malloc_dev.init(); return 1; } if (ptr < MEM_MAX_SIZE) { uint32_t index = ptr / MEM_BLOCK_SIZE; uint32_t mem_block = malloc_dev.map[index]; for (i = 0; i < mem_block; i++) { malloc_dev.map[index + i] = 0; } return 0;}return 2;}代碼很簡單,就是從管理表找到內存塊的數量,然后依次清零,它的父級函數
void Free(void* ptr) {uint32_t offset;if (ptr == NULL) return; offset = (uint32_t)ptr - (uint32_t)malloc_dev.base; mem_free(offset);}我們從當前地址去找到管理表的偏移量,然后在調用子級函數做其他處理
好啦,現在內存管理的重要函數我們已經講完了,接下來我們就來測試下吧
int main () { malloc_dev.init(); char* a = (char*)Malloc(sizeof(char)*1); printf("a address:0x%p", a); int i; printf("Memory manahement table(dir--->)(size:%d):", MEM_TABLE_SIZE); for (i = 0; i < 32; i++) { printf("%d ", malloc_dev.map[i]); } printf(""); printf("a content:"); strcpy(a, "I Love You"); printf("%s", a); printf("used:%.2f", malloc_dev.perused()/1000.0); Free(a); printf("used:%.2f", malloc_dev.perused()/1000.0); return 0;}下面是運行結果
從圖中可以看到,變量a獲取到的內存地址,與我們在malloc里面通過相對偏移加上起始地址之后的地址是一致的,并且通過內存管理表可以看到占用的內存塊的個數,以及使用率。
這里不知道大家有沒有發現一個問題,我們申請一個char類型的內存,也就是一個字節,但是系統卻返回給我們的是一個內存塊!而這里我們內存塊的單位是32字節,也就是說我們還有31字節沒有使用到,資源浪費太多,但是如果我們縮小內存塊的單位,勢必管理表的數量又會增加,在每次分配或者釋放的時候,照成時間延時比較多,所以如何權衡內存塊的最小值,是一個難題。
但是我們還有其他更優秀的算法來彌補這個分塊管理的缺陷,這里我就不做過多的討論了,在單片機系統里面,我們對內存沒有很嚴格的要求,所以這個算法也就被經常使用了,因為單片機系統往往主頻小,運行速度比不上PC,像紅黑樹那些算法跑起來需要較多的時間。
同時,我也在公眾號:GeEes
關注并回復:002
或者關注頭條號并私信:002
都可以獲得本文的參考資料和全部內容!
非常感謝你的關注!
總結
以上是生活随笔為你收集整理的memset 结构体内指针_数据结构之线性表应用——内存管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringMVC和SpringBoot
- 下一篇: 拦截器中addInterceptor和e