走到今天,已經(jīng)開(kāi)始涉及到計(jì)算機(jī)核心一點(diǎn)的東西了---內(nèi)存管理。通過(guò)本實(shí)驗(yàn)的學(xué)習(xí),能夠較為深刻體會(huì)到“指針是c語(yǔ)言的靈魂”這句話(huà)的分量。自然對(duì)c語(yǔ)言的能力要求就高很多了。
最近有點(diǎn)亂,但是有關(guān)嵌入式系統(tǒng)的學(xué)習(xí)不曾怠慢過(guò)。本文是基于原子老師的c源碼,自己的學(xué)習(xí)的心得,只是對(duì)源碼作出自己的學(xué)習(xí)理解,同時(shí)也插補(bǔ)了一些涉及到的c語(yǔ)言知識(shí)。貼出本文不為別的,一來(lái)但愿能有有緣人看到本文,提出指正;二來(lái),為了那些不眠的夜,安慰一下自己。
1, 內(nèi)存管理簡(jiǎn)介
內(nèi)存管理,是指軟件運(yùn)行時(shí)對(duì)計(jì)算機(jī)內(nèi)存資源的分配和使用的技術(shù)。其最主要的目的是如何高效,快速的分配,并且在適當(dāng)?shù)臅r(shí)候釋放和回收內(nèi)存資源。內(nèi)存管理的實(shí)現(xiàn)方法有很多種,他們其實(shí)最終都是要實(shí)現(xiàn)2個(gè)函數(shù):malloc和free;malloc函數(shù)用于內(nèi)存申請(qǐng),free函數(shù)用于內(nèi)存釋放。
先回顧一下c語(yǔ)言知識(shí):計(jì)算機(jī)內(nèi)存一般分為靜態(tài)存儲(chǔ)區(qū)用以存儲(chǔ)全局變量或常量和動(dòng)態(tài)存儲(chǔ)區(qū)用以存儲(chǔ)函數(shù)內(nèi)部變量或形參或函數(shù)運(yùn)算結(jié)果。malloc()函數(shù)的作用是請(qǐng)求系統(tǒng)在內(nèi)存的動(dòng)態(tài)存儲(chǔ)區(qū)分配若干個(gè)字節(jié)的存儲(chǔ)空間,函數(shù)的返回值是首字節(jié)地址,可見(jiàn)malloc()函數(shù)是指針類(lèi)型。free(P)的作用是釋放指針變量P所指向的動(dòng)態(tài)空間。
本章,我們介紹一種比較簡(jiǎn)單的辦法來(lái)實(shí)現(xiàn):分塊式內(nèi)存管理。下面我們介紹一下該方法的實(shí)現(xiàn)原理,如圖所示(示意圖):
內(nèi)存塊1 內(nèi)存塊2 內(nèi)存塊3 ……內(nèi)存塊n 內(nèi)存池
| | | |
第1項(xiàng) 第2項(xiàng) 第3項(xiàng) ……第n項(xiàng) 內(nèi)存管理表
<<-----分配方向
|
malloc,free等函數(shù)
圖解:從上圖可以看出,分塊式內(nèi)存管理由內(nèi)存池和內(nèi)存管理表兩部分組成。內(nèi)存池被等分為n塊,對(duì)應(yīng)的內(nèi)存管理表,大小也為n,內(nèi)存管理表的每一個(gè)項(xiàng)對(duì)應(yīng)內(nèi)存池的一塊內(nèi)存。
內(nèi)存管理表的項(xiàng)值代表的意義為:當(dāng)該項(xiàng)值為0的時(shí)候,代表對(duì)應(yīng)的內(nèi)存塊未被占用,當(dāng)該項(xiàng)值非零的時(shí)候,代表該項(xiàng)對(duì)應(yīng)的內(nèi)存塊已經(jīng)被占用,其數(shù)值則代表被連續(xù)占用的內(nèi)存塊數(shù)。比如某項(xiàng)值為10,那么說(shuō)明包括本項(xiàng)對(duì)應(yīng)的內(nèi)存塊在內(nèi),總共分配了10個(gè)內(nèi)存塊給外部的某個(gè)指針。
內(nèi)寸分配方向如圖所示,是從頂à底的分配方向。即首先從最末端開(kāi)始找空內(nèi)存。當(dāng)內(nèi)存管理剛初始化的時(shí)候,內(nèi)存表全部清零,表示沒(méi)有任何內(nèi)存塊被占用。
分配原理:
當(dāng)指針p調(diào)用malloc申請(qǐng)內(nèi)存的時(shí)候,先判斷p要分配的內(nèi)存塊數(shù)(m),然后從第n項(xiàng)開(kāi)始,向下查找,直到找到m塊連續(xù)的空內(nèi)存塊(即對(duì)應(yīng)內(nèi)存管理表項(xiàng)為0),然后將這m個(gè)內(nèi)存管理表項(xiàng)的值都設(shè)置為m(標(biāo)記被用),最后,把最后的這個(gè)空內(nèi)存塊的地址返回指針p,完成一次分配。注意,如果當(dāng)內(nèi)存不夠的時(shí)候(找到最后也沒(méi)找到連續(xù)的m塊空閑內(nèi)存),則返回NULL(空指針)給p,表示分配失敗。
釋放原理:
當(dāng)p申請(qǐng)的內(nèi)存用完,需要釋放的時(shí)候,調(diào)用free函數(shù)實(shí)現(xiàn)。free函數(shù)先判斷p指向的內(nèi)存地址所對(duì)應(yīng)的內(nèi)存塊,然后找到對(duì)應(yīng)的內(nèi)存管理表項(xiàng)目,得到p所占用的內(nèi)存塊數(shù)目m(內(nèi)存管理表項(xiàng)目的值就是所分配內(nèi)存塊的數(shù)目),將這m個(gè)內(nèi)存管理表項(xiàng)目的值都清零,標(biāo)記釋放,完成一次內(nèi)存釋放。
關(guān)于分塊式內(nèi)存管理的原理,我們就介紹到這里。
2, 硬件設(shè)計(jì):
本章實(shí)驗(yàn)功能簡(jiǎn)介:開(kāi)機(jī)后,顯示提示信息,等待外部輸入。KEY0用于申請(qǐng)內(nèi)存,每次申請(qǐng)2K字節(jié)內(nèi)存。KEY1用于寫(xiě)數(shù)據(jù)到申請(qǐng)到的內(nèi)存里面。KEY2用于釋放內(nèi)存。WK_UP用于切換操作內(nèi)存區(qū)(內(nèi)部?jī)?nèi)存/外部?jī)?nèi)存)。DS0用于指示程序運(yùn)行狀態(tài)。本章我們還可以通過(guò)USMART調(diào)試,測(cè)試內(nèi)存管理函數(shù)。
本實(shí)驗(yàn)用到的硬件資源有:
1) 指示燈DS0
2) 四個(gè)按鍵
3) 串口 //USMART
4) TFTLCD模塊
5) IS62WV51216
3, 軟件設(shè)計(jì):
本章,我們將內(nèi)存管理部分單獨(dú)做一個(gè)分組,在工程目錄下新建一個(gè)MALLOC的文件夾,然后新建malloc.c和malloc.h兩個(gè)文件,將他們保存在MALLOC文件夾下。
在MDK新建一個(gè)MALLOC的組,然后將malloc.c文件加入到該組,并將MALLOC文件夾添加到頭文件包含路徑。
打開(kāi)malloc.c文件,輸入如下代碼:由于本實(shí)驗(yàn)涉及到的c語(yǔ)言知識(shí),尤其是指針知識(shí)較多,所以就邊用邊學(xué)
#include "malloc.h"
//內(nèi)存池(4字節(jié)對(duì)齊)
__align(4) u8 mem1base[MEM1_MAX_SIZE]; //內(nèi)部SRAM內(nèi)存池
/*
" u8 mem1base[MEM1_MAX_SIZE];"該數(shù)組是定義拿出內(nèi)部?jī)?nèi)存池的40K的空間來(lái)做實(shí)驗(yàn),為什么該數(shù)組是u8類(lèi)型?計(jì)算機(jī)內(nèi)存是以字節(jié)為單位的存儲(chǔ)空間,內(nèi)存中的每個(gè)字節(jié)都有唯一的編號(hào),這個(gè)編號(hào)就叫地址。在這里就是定義40K個(gè)元素,每個(gè)元素代表一個(gè)字節(jié)。整個(gè)數(shù)組就代表整個(gè)內(nèi)部SRAM內(nèi)存池的總?cè)萘考?0K個(gè)元字節(jié)的總空間容量。因?yàn)椴还苁谴鎯?chǔ)什么數(shù)據(jù)類(lèi)型內(nèi)存中的地址編號(hào)都是32位的,即每個(gè)地址編號(hào)可以容納4個(gè)字節(jié),而不同的數(shù)據(jù)類(lèi)型存儲(chǔ)在不同的內(nèi)存存儲(chǔ)區(qū),這就是為什么定義變量時(shí)一定要先聲明其數(shù)據(jù)類(lèi)型的原因。存儲(chǔ)一個(gè)字符需要一個(gè)字節(jié)的存儲(chǔ)空間,存儲(chǔ)一個(gè)short類(lèi)型需要2個(gè)字節(jié)的存儲(chǔ)空間,存儲(chǔ)一個(gè)int或float需要4個(gè)字節(jié)空間,就如同PLC內(nèi)存中的字節(jié),字,雙字的定義規(guī)則一樣(如字節(jié)MB0,MB1,MB0和MB1構(gòu)成MW0;MW0和MW2構(gòu)成32位的雙字DW0,DW4,DW8)。“__align(4)”就是規(guī)定4個(gè)字節(jié)對(duì)齊,即每個(gè)32的地址編號(hào)存儲(chǔ)一個(gè)數(shù)據(jù)類(lèi)型?比如,字符存儲(chǔ)區(qū)中地址編號(hào)MB0可以存儲(chǔ)一個(gè)字節(jié)即8個(gè)位的數(shù)據(jù),而存儲(chǔ)MB0這個(gè)地址編號(hào)是以32位的空間來(lái)存儲(chǔ),也就是說(shuō)不管是什么類(lèi)型數(shù)據(jù),存儲(chǔ)它的地址編號(hào)都是32的,所以指針值一定是32位的。
//“#define MEM1_MAX_SIZE 40*1024 //最大管理內(nèi)存 40K”,意思是mem1base[MEM1_MAX_SIZE]有40k個(gè)元素
*/
__align(4) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0X68000000)));//外部SRAM內(nèi)存池
//#define MEM2_MAX_SIZE 200*1024 //最大管理內(nèi)存200K,意思是mem2base[MEM2_MAX_SIZE]數(shù)組有200K個(gè)u8類(lèi)型元素,第一個(gè)元素的地址存儲(chǔ)在 //外部存儲(chǔ)器SRAM的0X68000000地址,
//內(nèi)存管理表
u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; //內(nèi)部SRAM內(nèi)存池MAP
/*
//#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE //內(nèi)存表大小,MEM1_MAX_SIZE/MEM1_BLOCK_SIZE==1250
//#define MEM1_BLOCK_SIZE 32 //內(nèi)存塊大小為32字節(jié);“MEM1_MAX_SIZE/MEM1_BLOCK_SIZE ”的含義是內(nèi)部SRAM內(nèi)存池總共40K字節(jié)的容量除以32個(gè)字節(jié),得到一共40K/32==1250個(gè)內(nèi)存塊;也就是說(shuō)將內(nèi)部SRAM內(nèi)存池劃為1250個(gè)內(nèi)存塊。
“u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE];”實(shí)際上就等于“u16 mem1mapbase[1250];”意思是定義一個(gè)有1250個(gè)內(nèi)存塊(元素)的數(shù)組,每個(gè)元素是u16類(lèi)型數(shù)據(jù);數(shù)組名“mem1mapbase”就是mem1mapbase[0](該數(shù)組的第一個(gè)元素它代表1250個(gè)內(nèi)存塊中的第一個(gè)內(nèi)存塊)的地址,也可以說(shuō)是指針常量;結(jié)合與之關(guān)聯(lián)的結(jié)構(gòu)體成員“u16 *memmap[2]={ mem1mapbase,mem2mapbase}”指針類(lèi)型數(shù)組;在這里“mem2mapbase”是外部?jī)?nèi)存的第一個(gè)內(nèi)存塊的地址,是個(gè)指針常量用以存放u16類(lèi)型數(shù)據(jù)的地址值;結(jié)合
“mymemset(mallco_dev.memmap[0], 0,memtblsize[0]*2);”函數(shù)分析:結(jié)合本程序和結(jié)構(gòu)體有關(guān)定義“u16 *memmap[2]; ”,首元素memmap[0]=mem1mapbase;也就是說(shuō)“mallco_dev.memmap[0]”在這里表示1250個(gè)內(nèi)部?jī)?nèi)存塊中第一個(gè)內(nèi)存塊的地址,根據(jù)“u16 *memmap[2]={ mem1mapbase,mem2mapbase}”推斷出“mallco_dev.memmap[0]”是u16類(lèi)型指針;
“memtblsize[0]”是什么意思呢?根據(jù)“const u32 memtblsize[2]={1250,6250};”可以得知memtblsize[0]==1250即內(nèi)部?jī)?nèi)存一共有1250個(gè)管理項(xiàng),
void mymemset(void *s,u8 c,u32 count)
{
u8 *xs = s;
while(count--)*xs++=c;
} //把u8類(lèi)型數(shù)據(jù)c填充到以指針變量s為首地址的內(nèi)存空間中,填充多少個(gè)數(shù)由count值決定
該函數(shù)的意思是把u8類(lèi)型的數(shù)據(jù)“c”填充到u16類(lèi)型指針元素memmap[0]中(根據(jù)結(jié)構(gòu)體定義“u16 *memmap[2]; ”,而memmap[0]=mem1mapbase),說(shuō)白了就是把u8類(lèi)型的數(shù)據(jù)“c”填充到1250個(gè)內(nèi)存塊中的count個(gè)內(nèi)存塊中。
而mallco_dev.memmap[memx]是16位的,為了將其全部清零,所以乘以2.
本例中,用到了指針類(lèi)型數(shù)組“u16 *memmap[2]={ mem1mapbase,mem2mapbase}”,為什么要定義指針類(lèi)型數(shù)組呢?mem1mapbase是數(shù)組
“u16 mem1mapbase[1250];”的首個(gè)元素地址(即*mem1mapbase等價(jià)于mem1mapbase[0]),而mem1mapbase[0]就代表內(nèi)部存儲(chǔ)器1250個(gè)存儲(chǔ)塊中的第一個(gè)存儲(chǔ)塊;根據(jù)結(jié)構(gòu)體賦值定義可知:memmap[0]=mem1mapbase。所以mem1mapbase就是“mallco_dev.memmap[0]”,即mem1mapbase是函數(shù)mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2)的第一個(gè)形參,因?yàn)?mem1mapbase等價(jià)于mem1mapbase[0]),而mem1mapbase[0]就代表內(nèi)部存儲(chǔ)器1250個(gè)存儲(chǔ)塊中的第一個(gè)存儲(chǔ)塊。結(jié)合
void mymemset(void *s,u8 c,u32 count)函數(shù)分析, mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2)函數(shù)的意思是:把0寫(xiě)入到1250個(gè)存儲(chǔ)塊中的第一個(gè)存儲(chǔ)塊中;這樣就將一個(gè)存儲(chǔ)塊的值賦值為0了。
推斷出“mallco_dev.memmap[0]”是u16類(lèi)型指針;
;
*/
u16 mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+MEM2_MAX_SIZE)));
/*
“#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE”
“#define MEM2_BLOCK_SIZE 32”
外部SRAM內(nèi)存池MAP,同理,“MEM2_MAX_SIZE/MEM2_BLOCK_SIZE”的含義是外部SRAM內(nèi)存池總共200K字節(jié)的容量除以32個(gè)字節(jié),得到一共200K/32==6250個(gè)內(nèi)存塊;也就是說(shuō)將外部SRAM內(nèi)存池劃為6250個(gè)內(nèi)存塊。
*/
//內(nèi)存管理參數(shù)
/*
內(nèi)存管理表“MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE”分別是1250和6250個(gè)“項(xiàng)”.
每個(gè)內(nèi)存分塊大小即內(nèi)部和外部SRAM每個(gè)內(nèi)存塊占有32個(gè)字節(jié)空間“MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE”分別是32個(gè)字節(jié);
內(nèi)存總大小“MEM1_MAX_SIZE,MEM2_MAX_SIZE”,分別是40K和200K個(gè)字節(jié)的總?cè)萘靠臻g
mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2);
*/
const u32 memtblsize[2]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE};//內(nèi)存管理表大小
const u32 memblksize[2]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE}; //內(nèi)存分塊大小
const u32 memsize[2]={MEM1_MAX_SIZE,MEM2_MAX_SIZE}; //內(nèi)存總大小
/*
struct _m_mallco_dev //內(nèi)存管理控制器,定義一個(gè)結(jié)構(gòu)體類(lèi)型數(shù)據(jù),或結(jié)構(gòu)體變量,
{
void (*init)(u8); //初始化
u8 (*perused)(u8); //內(nèi)存使用率
u8 *membase[2]; //內(nèi)存池 管理2個(gè)區(qū)域的內(nèi)存 mem1base,mem2base內(nèi)存池
u16 *memmap[2]; //內(nèi)存管理狀態(tài)表 mem1mapbase(==1250塊),mem2mapbase(6250), //內(nèi)存管理狀態(tài)表
u8 memrdy[2]; //內(nèi)存管理是否就緒
};
1,結(jié)構(gòu)體成員“void (*init)(u8);”是定義了一個(gè)指向函數(shù)的指針變量,該指針變量名是init;void表示該函數(shù)沒(méi)有返回值(函數(shù)的數(shù)據(jù)類(lèi)型由返回值決定);u8是函數(shù)的形參。指向函數(shù)的指針變量格式:數(shù)據(jù)類(lèi)型 + (*變量名)(形參)
本例中:
void mem_init(u8 memx)
{
mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2);//內(nèi)存狀態(tài)表數(shù)據(jù)清零 memx:所屬內(nèi)存塊,即幾號(hào)內(nèi)存塊
mymemset(mallco_dev.membase[memx], 0,memsize[memx]); //內(nèi)存池所有數(shù)據(jù)清零
mallco_dev.memrdy[memx]=1;//內(nèi)存管理初始化OK
}
也就是說(shuō),本例中用指向函數(shù)的指針變量來(lái)表示函數(shù)。c語(yǔ)言規(guī)定函數(shù)名就是函數(shù)的入口地址,也就是說(shuō)函數(shù)名也是一個(gè)指針,指向函數(shù)的入口,根據(jù)這個(gè)原理,可以將指向函數(shù)的指針作為函數(shù)的參數(shù)調(diào)用,可以在不同的情況調(diào)用不同的函數(shù);如果一個(gè)指向函數(shù)的指針變量等于函數(shù)名就可以說(shuō)該指向函數(shù)的指針變量指向了該函數(shù),那么指針變量與函數(shù)就是一樣的了。比如:“mem_init(memx);”就等同于“mallco_dev.init(memx);”
2,指針類(lèi)型數(shù)組“u8 *membase[2];”,意思是該指針類(lèi)型數(shù)組有兩個(gè)“char *”類(lèi)型的指針元素或者說(shuō)有兩個(gè)“u8 *”類(lèi)型指針元素;為什么要定義“u8 *”類(lèi)型呢?因?yàn)閮?nèi)存存儲(chǔ)區(qū)是根據(jù)數(shù)據(jù)類(lèi)型來(lái)劃分的,如果不明確聲明類(lèi)型就亂套了。
在C語(yǔ)言和C++語(yǔ)言中,數(shù)組元素全為指針的數(shù)組稱(chēng)為指針數(shù)組。一維指針數(shù)組的定義形式為:“類(lèi)型名 *數(shù)組標(biāo)識(shí)符[數(shù)組長(zhǎng)度]”。
例如,一個(gè)一維指針數(shù)組的定義:int *ptr_array[10]。該指針數(shù)組有10個(gè)元素,每個(gè)元素都是int類(lèi)型的指針即“int *”類(lèi)型;
指針類(lèi)型數(shù)組“u8 *membase[2];”的賦值是mem1base,mem2base, “mem1base,mem2base”分別是內(nèi)部?jī)?nèi)存池和外部?jī)?nèi)存池的數(shù)組名,是指針常量即首元素的地址;因?yàn)槭孪纫呀?jīng)定義“u8 mem1base[MEM1_MAX_SIZE]”即“u8 mem1base[40K];”。
*/
//內(nèi)存管理控制器,結(jié)構(gòu)體變量賦值,即初始化
struct _m_mallco_dev mallco_dev=
{
mem_init, //內(nèi)存初始化,將函數(shù)名“mem_init”賦給結(jié)構(gòu)體成員“void (*init)(u8);”即指向函數(shù)的指針變量,
mem_perused, //內(nèi)存使用率
mem1base,mem2base, //內(nèi)存池
mem1mapbase,mem2mapbase, //內(nèi)存管理狀態(tài)表,mem1mapbase(1250項(xiàng)),mem2mapbase(6250項(xiàng))
0,0, //內(nèi)存管理未就緒
};
/*
1,“void *des”無(wú)類(lèi)型指針,不能指向具體的數(shù)據(jù),“void *des”無(wú)類(lèi)型指針指向內(nèi)存中的數(shù)據(jù)類(lèi)型由用戶(hù)自己確定,如malloc()函數(shù)的返回值就是“void *des”無(wú)類(lèi)型指針,因?yàn)閙alloc()函數(shù)的返回值是不確定的是根據(jù)形參的數(shù)據(jù)類(lèi)型確定的
2,“void mymemcpy(void *des,void *src,u32 n) ”函數(shù)的理解:
des是指針,但是不確定指向什么類(lèi)型的數(shù)據(jù),換句話(huà)說(shuō)des指針存儲(chǔ)的什么類(lèi)型數(shù)據(jù)不確定,“u8 *xdes=des;”將des指針存儲(chǔ)的數(shù)據(jù)
存儲(chǔ)到一個(gè)新的“u8 *”類(lèi)型指針xdes中;“u8 *xsrc=src;”同理。
“*xdes++=*xsrc++; ”,當(dāng)*xsrc++(自增)時(shí),即指針“src”指針自增,也就是說(shuō)把“src”指針逐位復(fù)制到des目標(biāo)指針去了。復(fù)制個(gè)數(shù)就是n。
3,“*P”的意義:a),“*P”就是以指針變量P的內(nèi)容(P的內(nèi)容就是指針變量P里存儲(chǔ)的某一類(lèi)型數(shù)據(jù)的指針值)為地址的變量;b),指針運(yùn)算符“*”如果是在定義變量時(shí)候加在前面,意思是這個(gè)變量是指針變量,如 char *a;如果是在訪(fǎng)問(wèn)指針變量的時(shí)候加在前面(如*a),意思是取指針變量指向的值,如 char b=*a; 上面定義了a是一個(gè)字符指針,這里就是把指針變量a指向的值取出來(lái)并賦給b。
*/
//復(fù)制內(nèi)存,作用是將源地址的內(nèi)容復(fù)制到目標(biāo)地址
//*des:目的地址
//*src:源地址
//n:需要復(fù)制的內(nèi)存長(zhǎng)度(字節(jié)為單位)
void mymemcpy(void *des,void *src,u32 n)
{ //“void *des”無(wú)類(lèi)型指針,不能指向具體的數(shù)據(jù),“void *des”無(wú)類(lèi)型指針指向內(nèi)存中的數(shù)據(jù)類(lèi)型由用戶(hù)自己確定
u8 *xdes=des;//目標(biāo)地址,“*xdes”轉(zhuǎn)換成u8類(lèi)型,也可以理解為把目的地地址des存儲(chǔ)到xdes指針中
u8 *xsrc=src;
while(n--)*xdes++=*xsrc++;
}
//設(shè)置內(nèi)存
//*s:內(nèi)存首地址
//c :要設(shè)置的值
//count:需要設(shè)置的內(nèi)存大小(字節(jié)為單位)
void mymemset(void *s,u8 c,u32 count)
{
u8 *xs = s;
while(count--)*xs++=c;
} //以*s為內(nèi)存首地址的count個(gè)字節(jié)中,填充c,即把c寫(xiě)入到*s為首地址的內(nèi)存中,個(gè)數(shù)多少由count值決定
//內(nèi)存管理初始化
//memx:所屬內(nèi)存塊,要么SRAMEX==1(外部?jī)?nèi)存);要么SRAMIN(內(nèi)部?jī)?nèi)存)==0
/*
const u32 memtblsize[2]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE};//內(nèi)存管理表大小
const u32 memblksize[2]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE}; //內(nèi)存分塊大小
const u32 memsize[2]={MEM1_MAX_SIZE,MEM2_MAX_SIZE}; //內(nèi)存總大小
*/
void mem_init(u8 memx) //如“mem_init(SRAMIN);”表示內(nèi)部?jī)?nèi)存塊
{ //memmap,是16位的,mymemset,設(shè)置是針對(duì)8位的,那么1個(gè)16位的數(shù)據(jù)是不是2個(gè)8位組成的啊?!
mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2);//內(nèi)存狀態(tài)表數(shù)據(jù)清零
//把u8類(lèi)型的數(shù)據(jù)“0”填充到u16類(lèi)型指針元素memmap[0]中(根據(jù)結(jié)構(gòu)體定義“u16 *memmap[2]; ”),memmap[0]=mem1mapbase==1250,
//也就是說(shuō)“mallco_dev.memmap[memx]”在這里表示1250個(gè)內(nèi)部?jī)?nèi)存塊用以存儲(chǔ)u16類(lèi)型指針,
//“memtblsize[memx]”是什么呢?memtblsize[memx]即memtblsize[0]==1250個(gè)內(nèi)部?jī)?nèi)存管理表,
//而mallco_dev.memmap[memx]是16位的,為了將其全部清零,所以乘以2.
mymemset(mallco_dev.membase[memx], 0,memsize[memx]); //內(nèi)存池所有數(shù)據(jù)清零
//memsize[0]==40K字節(jié)空間, mallco_dev.membase[memx]==40K字節(jié)空間,
mallco_dev.memrdy[memx]=1; //內(nèi)存管理初始化OK
}
/*
*/
//獲取內(nèi)存使用率
//memx:所屬內(nèi)存塊,要么SRAMEX==1(外部?jī)?nèi)存);要么SRAMIN(內(nèi)部?jī)?nèi)存)==0
//返回值:使用率(0~100)
u8 mem_perused(u8 memx)
{
u32 used=0;
u32 i;
for(i=0;i<memtblsize[memx];i++)
{
if(mallco_dev.memmap[memx][i])used++;
} //mallco_dev.memmap[memx][i]是二維數(shù)組。當(dāng)內(nèi)存塊初始化后該值為0,
return (used*100)/(memtblsize[memx]); //used*100,乘以100是將小數(shù)變成整數(shù)
}
//內(nèi)存分配(內(nèi)部調(diào)用)
//memx:所屬內(nèi)存塊
//size:要分配的內(nèi)存大小(字節(jié)數(shù))
//返回值:0XFFFFFFFF,代表錯(cuò)誤;其他,內(nèi)存偏移地址
//向memx存儲(chǔ)器申請(qǐng)size個(gè)字節(jié)的連續(xù)存儲(chǔ)空間,并將size個(gè)字節(jié)中首個(gè)字節(jié)的地址偏移值標(biāo)注出來(lái),注意是地址偏移值而不是地址。
u32 mem_malloc(u8 memx,u32 size)
{
signed long offset=0;
u16 nmemb; //需要的內(nèi)存塊數(shù)
u16 cmemb=0;//連續(xù)空內(nèi)存塊數(shù)
u32 i;
if(!mallco_dev.memrdy[memx])mallco_dev.init(memx);//未初始化,先執(zhí)行初始化
/*
“mallco_dev.init(memx);”是什么意思?mallco_dev.init(memx)是結(jié)構(gòu)體變量mallco_dev的一個(gè)成員,本句中就是對(duì)結(jié)構(gòu)體成員的引用,即執(zhí)行
mem_init(u8 memx)函數(shù)的意思;如何引用結(jié)構(gòu)體中指向函數(shù)的指針變量成員?既然是指向函數(shù)的指針變量且有賦值,在引用時(shí)按照格式:
結(jié)構(gòu)體變量名.指向函數(shù)的指針變量名(形參);
*/
if(size==0)return 0XFFFFFFFF;//不需要分配 memblksize[memx]==32
nmemb=size/memblksize[memx]; //獲取需要分配的連續(xù)內(nèi)存塊數(shù)
/*
c語(yǔ)言規(guī)定:除法的運(yùn)算結(jié)果與運(yùn)算對(duì)象的數(shù)據(jù)類(lèi)型有關(guān),兩個(gè)數(shù)都是int則商(即結(jié)果)是int,若商(即結(jié)果)有小數(shù)則省略掉小數(shù)點(diǎn)部分。本例中
size和memblksize[memx]都是int,所以結(jié)果只能是int。假設(shè)size<32,則nmemb==0;
c語(yǔ)言規(guī)定取余運(yùn)算的運(yùn)算對(duì)象必須是int。當(dāng)小數(shù)對(duì)大數(shù)取余時(shí)余(即結(jié)果)是小數(shù)本身;例如,在“if(size%memblksize[memx])nmemb++;”中 ,
假設(shè)size<32,則size%memblksize[memx]的結(jié)果是size值本身,所以執(zhí)行“nmemb++;”運(yùn)算,這時(shí)運(yùn)算結(jié)果是nmemb==1;如果size是32的整數(shù)倍則不執(zhí)行
“nmemb++;”運(yùn)算;
memtblsize[0]==1250,memtblsize[1]==6250,
mallco_dev.memmap[memx][offset]是什么意思?
*/
if(size%memblksize[memx])nmemb++;
for(offset=memtblsize[memx]-1;offset>=0;offset--)//搜索整個(gè)內(nèi)存控制區(qū)
{
if(!mallco_dev.memmap[memx][offset])cmemb++;//連續(xù)空內(nèi)存塊數(shù)增加,offset從1249->0變化
/*
如,{ memmap[0][149],memmap[0][148],...memmap[0][1],memmap[0][0]};實(shí)際上可以把“mallco_dev.memmap[memx][offset]”視為具有1250個(gè)變量的
一維數(shù)組,每個(gè)元素對(duì)應(yīng)的實(shí)際意義是對(duì)應(yīng)的一個(gè)內(nèi)存塊,順序是offset從1249(高)->0(低)變化;如果哪個(gè)變量等于0(即空閑)就執(zhí)行
“cmemb++;”操作,這樣就可以計(jì)算出連續(xù)空閑內(nèi)存塊數(shù)cmemb;切記!目的是要獲取連續(xù)的空閑的內(nèi)存塊數(shù)!這樣就必須結(jié)合下一句
“else cmemb=0;”來(lái)分析;如果沒(méi)有出現(xiàn)連續(xù)的空閑內(nèi)存塊(即數(shù)組順序相連的變量值沒(méi)有出現(xiàn)類(lèi)似“0,0,0,0,0”這樣的情況),程序會(huì)執(zhí)行下一語(yǔ)
句“else cmemb=0;”即把上面的“cmemb”統(tǒng)計(jì)值清零,這樣程序就會(huì)在for循環(huán)里面繼續(xù)尋找符合“if(cmemb==nmemb)”條件的狀態(tài)出現(xiàn),
如果for循環(huán)執(zhí)行完了還沒(méi)有出現(xiàn)符合“if(cmemb==nmemb)”條件的狀態(tài),則返回0XFFFFFFFF結(jié)束本函數(shù)表示沒(méi)有找到符合條件的內(nèi)存塊。假
設(shè):size=65,那么nmemb就是3即需要獲取連續(xù)3個(gè)內(nèi)存塊來(lái)存放65個(gè)字節(jié),再假設(shè)數(shù)組順序相連的變量值出現(xiàn)了類(lèi)似“0,0,0,0,0”這樣的情況(即有
連續(xù)4個(gè)空閑的內(nèi)存塊),這時(shí)就出現(xiàn)了符合“if(cmemb==nmemb)”條件的狀態(tài),即當(dāng)cmemb計(jì)數(shù)計(jì)到3的時(shí)候(即出現(xiàn)了連續(xù)相連的3個(gè)內(nèi)存塊)就
符合“cmemb==nmemb”了,程序就自然進(jìn)入“if(cmemb==nmemb)”語(yǔ)句。
offset*memblksize[memx]代表什么呢?offset的取值范圍是0-1249,memblksize[memx]代表每個(gè)內(nèi)存塊的字節(jié)數(shù)即32,offset*memblksize[memx]就
是返回偏移地址值;也就是把連續(xù)空閑的內(nèi)存塊對(duì)應(yīng)的地址的首地址值標(biāo)注出來(lái)。
*/
else cmemb=0; //連續(xù)內(nèi)存塊清零
if(cmemb==nmemb) //找到了連續(xù)nmemb個(gè)空內(nèi)存塊
{
for(i=0;i<nmemb;i++) //標(biāo)注內(nèi)存塊非空,以免下一個(gè)for循環(huán)時(shí)再次將該空間計(jì)入
{
mallco_dev.memmap[memx][offset+i]=nmemb;
}
return (offset*memblksize[memx]);//返回偏移地址
}
}
return 0XFFFFFFFF;//未找到符合分配條件的內(nèi)存塊
}
//釋放內(nèi)存(內(nèi)部調(diào)用)
//memx:所屬內(nèi)存塊
//offset:內(nèi)存地址偏移
//返回值:0,釋放成功;1,釋放失敗;
u8 mem_free(u8 memx,u32 offset)
{
int i;
if(!mallco_dev.memrdy[memx])//未初始化,先執(zhí)行初始化
{
mallco_dev.init(memx); //本句等價(jià)于“mem_init(memx);”
return 1;//未初始化
}
if(offset<memsize[memx])//以免偏移在內(nèi)存池內(nèi). memsize[memx]==40K字節(jié)
{
int index=offset/memblksize[memx]; //偏移所在內(nèi)存塊號(hào)碼 memblksize[memx]==32,
int nmemb=mallco_dev.memmap[memx][index]; //內(nèi)存塊數(shù)量
for(i=0;i<nmemb;i++) //內(nèi)存塊清零
{
mallco_dev.memmap[memx][index+i]=0;
}
return 0;
}else return 2;//偏移超區(qū)了.
}
//釋放內(nèi)存(外部調(diào)用)
//memx:所屬內(nèi)存塊
//ptr:內(nèi)存首地址
void myfree(u8 memx,void *ptr)
{
u32 offset;
if(ptr==NULL)return;//地址為0.
offset=(u32)ptr-(u32)mallco_dev.membase[memx];
mem_free(memx,offset);//釋放內(nèi)存
}
//分配內(nèi)存(外部調(diào)用)
//memx:所屬內(nèi)存塊
//size:內(nèi)存大小(字節(jié))
//返回值:分配到的內(nèi)存首地址.
//在memx存儲(chǔ)器中,找出size個(gè)字節(jié)的連續(xù)空閑的內(nèi)存空間,并將連續(xù)空閑的內(nèi)存空間指針值標(biāo)注出來(lái);返回值就是這個(gè)指針值
/*
mallco_dev.membase[memx]即mallco_dev.membase[0]代表MCU內(nèi)部存儲(chǔ)器的40K字節(jié)中的第一個(gè)字節(jié)變量的地址,是u8類(lèi)型指針變量,也就是說(shuō)一個(gè)字節(jié)占用一個(gè)地址;換句話(huà)說(shuō),把內(nèi)部存儲(chǔ)器的40K字節(jié)的地址定義為一個(gè)“u8 mem1base[MEM1_MAX_SIZE]”數(shù)組,指針類(lèi)型數(shù)組“u8 *membase[2];”的賦值是{mem1base,mem2base},而“mem1base,mem2base”分別是內(nèi)部?jī)?nèi)存池和外部?jī)?nèi)存池的數(shù)組名,各自首元素的地址亦是個(gè)指針常量;因?yàn)槭孪纫呀?jīng)定義
“u8 mem1base[MEM1_MAX_SIZE]”即“u8 mem1base[40K];”。如何理解“(void*)((u32)mallco_dev.membase[memx]+offset); ”呢?
1),已經(jīng)說(shuō)過(guò)mallco_dev.membase[memx]是首個(gè)變量的地址即40k字節(jié)中首個(gè)字節(jié)的地址值;
2),“offset”是:向memx存儲(chǔ)器申請(qǐng)size個(gè)字節(jié)的連續(xù)空閑存儲(chǔ)空間,這個(gè)找到的連續(xù)空閑空間當(dāng)中首個(gè)字節(jié)的地址偏移值就是offset,offset==32(將32個(gè)字節(jié)空間組成一個(gè)內(nèi)存塊)*內(nèi)存塊號(hào)(如,假設(shè)向內(nèi)部存儲(chǔ)器申請(qǐng)64個(gè)字節(jié)的連續(xù)空閑存儲(chǔ)空間,通過(guò)“mem_malloc(memx,size); ”函數(shù)得到在第五個(gè)存儲(chǔ)塊開(kāi)始有連續(xù)2個(gè)存儲(chǔ)快空閑可供使用(假設(shè)是5號(hào)和4號(hào)存儲(chǔ)快),因?yàn)槊總(gè)存儲(chǔ)快有32個(gè)字節(jié)即有32個(gè)地址編號(hào),4*32==128(這里的4是指第四塊),5*32==160(這里的5是指第五塊),那么這個(gè)160就是40K個(gè)字節(jié)編號(hào)當(dāng)中的地址偏移值offset,即128-192號(hào)就是第四塊和第五塊內(nèi)存塊所對(duì)應(yīng)的指針編號(hào));注意offset是地址偏移值而不是地址;為什么要引入地址偏移值這個(gè)概念呢?假設(shè)第一個(gè)字節(jié)的地址值是0x0000 6800,那么就知道(0x0000 6800+160)的值就是第五塊內(nèi)存的指針。
3),“(u32)mallco_dev.membase[memx]”代表指針類(lèi)型數(shù)組,意義是內(nèi)部存儲(chǔ)器40K字節(jié)中的第一個(gè)字節(jié)變量的地址,原來(lái)存放的是u8類(lèi)型數(shù)據(jù)的地址,現(xiàn)在強(qiáng)制類(lèi)型轉(zhuǎn)換擴(kuò)展為u32類(lèi)型;
4),(void*)((u32)mallco_dev.membase[memx]+offset); 轉(zhuǎn)換為無(wú)類(lèi)型指針,指針值是32位,由此可知,“void *mymalloc(u8 memx,u32 size)”函數(shù)的返回值就是一個(gè)指針,即形參size所指向的由高向低的首個(gè)指針值;“void *mymalloc(u8 memx,u32 size)”是個(gè)指針類(lèi)型函數(shù),只能賦給指針。
*/
void *mymalloc(u8 memx,u32 size) //p=mymalloc(sramx,2048)
{
u32 offset;
offset=mem_malloc(memx,size);
if(offset==0XFFFFFFFF)return NULL;
else return (void*)((u32)mallco_dev.membase[memx]+offset);
}
//重新分配內(nèi)存(外部調(diào)用)
//memx:所屬內(nèi)存塊
//*ptr:舊內(nèi)存首地址
//size:要分配的內(nèi)存大小(字節(jié))
//返回值:新分配到的內(nèi)存首地址.
void *myrealloc(u8 memx,void *ptr,u32 size)
{
u32 offset;
offset=mem_malloc(memx,size);
if(offset==0XFFFFFFFF)return NULL;
else
{
mymemcpy((void*)((u32)mallco_dev.membase[memx]+offset),ptr,size); //拷貝舊內(nèi)存內(nèi)容到新內(nèi)存
// 把size個(gè)字節(jié)指針ptr復(fù)制到“((u32)mallco_dev.membase[memx]+offset)”,
myfree(memx,ptr); //釋放舊內(nèi)存,因?yàn)樵趍em_malloc(memx,size)中已經(jīng)將連續(xù)空閑內(nèi)存塊標(biāo)注為1(已被占用),清除掉原來(lái)的標(biāo)記
return (void*)((u32)mallco_dev.membase[memx]+offset); //返回新內(nèi)存首地址,無(wú)類(lèi)型指針
}
}
頭文件:
#ifndef __MALLOC_H
#define __MALLOC_H
typedef unsigned long u32;
typedef unsigned short u16;
typedef unsigned char u8;
#ifndef NULL
#define NULL 0
#endif
#define SRAMIN 0 //內(nèi)部?jī)?nèi)存池
#define SRAMEX 1 //外部?jī)?nèi)存池
//mem1內(nèi)存參數(shù)設(shè)定.mem1完全處于內(nèi)部SRAM里面
#define MEM1_BLOCK_SIZE 32 //內(nèi)存塊大小為32字節(jié)
#define MEM1_MAX_SIZE 40*1024 //最大管理內(nèi)存 40K
#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE //內(nèi)存表大小
//mem2內(nèi)存參數(shù)設(shè)定.mem2的內(nèi)存池處于外部SRAM里面,其他的處于內(nèi)部SRAM里面
#define MEM2_BLOCK_SIZE 32 //內(nèi)存塊大小為32字節(jié)
#define MEM2_MAX_SIZE 200*1024 //最大管理內(nèi)存200K
#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE //內(nèi)存表大小
struct _m_mallco_dev //內(nèi)存管理控制器
{
void (*init)(u8); //初始化
u8 (*perused)(u8); //內(nèi)存使用率
u8 *membase[2]; //內(nèi)存池 管理2個(gè)區(qū)域的內(nèi)存
u16 *memmap[2]; //內(nèi)存管理狀態(tài)表
u8 memrdy[2]; //內(nèi)存管理是否就緒
};
extern struct _m_mallco_dev mallco_dev; //在mallco.c里面定義,定義全局變量,結(jié)構(gòu)體變量mallco_dev
void mymemset(void *s,u8 c,u32 count); //設(shè)置內(nèi)存
void mymemcpy(void *des,void *src,u32 n);//復(fù)制內(nèi)存
void mem_init(u8 memx); //內(nèi)存管理初始化函數(shù)(外/內(nèi)部調(diào)用)
u32 mem_malloc(u8 memx,u32 size); //內(nèi)存分配(內(nèi)部調(diào)用)
u8 mem_free(u8 memx,u32 offset); //內(nèi)存釋放(內(nèi)部調(diào)用)
u8 mem_perused(u8 memx); //獲得內(nèi)存使用率(外/內(nèi)部調(diào)用)
////////////////////////////////////////////////////////////////////////////////
//用戶(hù)調(diào)用函數(shù)
void myfree(u8 memx,void *ptr); //內(nèi)存釋放(外部調(diào)用)
void *mymalloc(u8 memx,u32 size); //內(nèi)存分配(外部調(diào)用)
void *myrealloc(u8 memx,void *ptr,u32 size);//重新分配內(nèi)存(外部調(diào)用)
#endif
這部分代碼,定義了很多關(guān)鍵數(shù)據(jù),比如內(nèi)存塊大小的定義:MEM1_BLOCK_SIZE和MEM2_BLOCK_SIZE,都是32字節(jié)。內(nèi)存池總大小,內(nèi)部為40K,外部為200K(最大支持到近1M字節(jié),不過(guò)為了方便演示,這里只管理200K內(nèi)存)。MEM1_ALLOC_TABLE_SIZE和MEM2_ALLOC_TABLE_SIZE,則分別代表內(nèi)存池1和2的內(nèi)存管理表大小。
從這里可以看出,如果內(nèi)存分塊越小,那么內(nèi)存管理表就越大,當(dāng)分塊為2字節(jié)1個(gè)塊的時(shí)候,內(nèi)存管理表就和內(nèi)存池一樣大了(管理表的每項(xiàng)都是u16類(lèi)型)。顯然是不合適的,我們這里取32字節(jié),比例為1:16,內(nèi)存管理表相對(duì)就比較小了。
主函數(shù)部分:
int main(void)
{
u8 key;
u8 i=0;
u8 *p=0;
u8 *tp=0;
u8 paddr[18]; //存放的內(nèi)容“P Addr:+p地址的ASCII值”
u8 sramx=0; //默認(rèn)為內(nèi)部sram
Stm32_Clock_Init(9); //系統(tǒng)時(shí)鐘設(shè)置
uart_init(72,9600); //串口初始化為9600
delay_init(72); //延時(shí)初始化
led_init(); //初始化與LED連接的硬件接口
LCD_Init(); //初始化LCD
usmart_dev.init(72); //初始化USMART
Key_Init(); //按鍵初始化
FSMC_SRAM_Init(); //初始化外部SRAM,因?yàn)橛玫搅送獠縮ram
mem_init(SRAMIN); //初始化內(nèi)部?jī)?nèi)存池,SRAMIN==0
mem_init(SRAMEX); //初始化外部?jī)?nèi)存池,SRAMEX==1
POINT_COLOR=RED;//設(shè)置字體為紅色
LCD_ShowString(60,50,200,16,16,"WarShip STM32");
LCD_ShowString(60,70,200,16,16,"MALLOC TEST");
LCD_ShowString(60,90,200,16,16,"WANG YAN");
LCD_ShowString(60,110,200,16,16,"2013/12/16");
LCD_ShowString(60,130,200,16,16,"key_right:Malloc key_left:Free");
LCD_ShowString(60,150,200,16,16,"wake_up:SRAMx key_down:Read");
POINT_COLOR=BLUE;//設(shè)置字體為藍(lán)色
LCD_ShowString(60,170,200,16,16,"SRAMIN");
LCD_ShowString(60,190,200,16,16,"SRAMIN USED: %");
LCD_ShowString(60,210,200,16,16,"SRAMEX USED: %");
while(1)
{
key=Key_Scan(0);//不支持連按
switch(key)
{
case 0://沒(méi)有按鍵按下
break;
case key_right: //KEY0按下
p=mymalloc(sramx,2048);//申請(qǐng)2K字節(jié),即64個(gè)內(nèi)存塊的空間
if(p!=NULL)sprintf((char*)p,"Memory Malloc Test%03d",i);//向p寫(xiě)入一些內(nèi)容
break;
case key_down: //KEY1按下
if(p!=NULL) //NULL==0;
{
sprintf((char*)p,"Memory Malloc Test%03d",i);//更新顯示內(nèi)容
// LCD_ShowString(60,270,200,16,16,p);
LCD_ShowString(60,250,200,16,16,p);//顯示P的內(nèi)容
printf("Memory Malloc Test%03d\n",i);//將“Memory Malloc Test”用串口輸出,利用串口助手可以看到輸出的結(jié)果
//"03"表示參數(shù)“i”的值只顯示3位,%-輸出控制符;d-將“i”以十進(jìn)制的形式輸出;i的范圍0--255;輸出參數(shù)可以是多個(gè),可以參考郝斌老師的相關(guān)視頻;
//輸出控制符包含:%Ld--L代表long類(lèi)型;%c--代表字符類(lèi)型;:%X--代表16進(jìn)制并大寫(xiě);
}
break;
case key_left: //KEY2按下
myfree(sramx,p);//釋放內(nèi)存
p=0; //指向空地址
break;
case wake_up: //KEY UP按下
sramx=!sramx;//切換當(dāng)前malloc/free操作對(duì)象
if(sramx)LCD_ShowString(60,170,200,16,16,"SRAMEX");
else LCD_ShowString(60,170,200,16,16,"SRAMIN");
break;
}
if(tp!=p)
{//在內(nèi)存paddr值處顯示:“P Addr:0X%08X”,“0X%08X”以大寫(xiě)16進(jìn)制顯示參數(shù)tp(32位),“08”表示8位數(shù)。0X AAAA AAAA
//剛進(jìn)入程序時(shí),因?yàn)閳?zhí)行了“mem_init(SRAMIN);”初始化函數(shù),所以p==0;所以L(fǎng)CD不會(huì)有顯示
//因?yàn)槌绦蛞婚_(kāi)始就有“u8 *tp=0;”,所以若不按下任何按鍵LCD就不會(huì)顯示下面的內(nèi)容(即“if(tp!=p)”控制的顯示內(nèi)容);
tp=p;//PAddr顯示的是指針p本身的地址值;指針值是u32類(lèi)型
sprintf((char*)paddr,"P Addr:0X%08X",(u32)tp);//將指針p本身的地址值在LCD上打印出來(lái)即顯示
LCD_ShowString(60,230,200,16,16,paddr); //顯示p的地址
if(p)
LCD_ShowString(60,250,200,16,16,p);//顯示P的內(nèi)容,即指針p內(nèi)存儲(chǔ)的數(shù)據(jù)“Memory Malloc Test%03d”
else LCD_Fill(60,250,239,266,WHITE); //p=0,清除顯示
}
delay_ms(10);
i++;
if((i%20)==0)//DS0閃爍.
{
LCD_ShowNum(60+96,190,mem_perused(SRAMIN),3,16);//顯示內(nèi)部?jī)?nèi)存使用率
LCD_ShowNum(60+96,210,mem_perused(SRAMEX),3,16);//顯示外部?jī)?nèi)存使用率
led0=!led0;
}
}
}
總結(jié):通過(guò)內(nèi)存管理的學(xué)習(xí),更加深刻的領(lǐng)會(huì)到指針是c語(yǔ)言的靈魂,對(duì)c語(yǔ)言的知識(shí)是一個(gè)鞏固和提高;同時(shí)也學(xué)習(xí)到了sprintf()函數(shù)的運(yùn)用技巧。
本章希望利用USMART調(diào)試內(nèi)存管理,所以在USMART里面添加了mymalloc和myfree兩個(gè)函數(shù),用于測(cè)試內(nèi)存分配和內(nèi)存釋放。大家可以通過(guò)USMART自行測(cè)試。
4,下載驗(yàn)證:
在代碼編譯成功之后,我們通過(guò)下載代碼到ALIENTEK戰(zhàn)艦STM32開(kāi)發(fā)板上,得到如圖所示界面:
可以看到,內(nèi)外內(nèi)存的使用率均為0%,說(shuō)明還沒(méi)有任何內(nèi)存被使用,此時(shí)我們按下KEY0,就可以看到內(nèi)部?jī)?nèi)存被使用5%(每按下一次申請(qǐng)2K的空間,lcd上顯示的使用率遞增5%;20*2K==40K)了,同時(shí)看到下面提示了指針p所指向的地址(其實(shí)就是被分配到的內(nèi)存地址)和內(nèi)容。多按幾次KEY0,可以看到內(nèi)存使用率持續(xù)上升(注意對(duì)比p的值,可以發(fā)現(xiàn)是遞減的,說(shuō)明是從頂部開(kāi)始分配內(nèi)存!),此時(shí)如果按下KEY2,可以發(fā)現(xiàn)內(nèi)存使用率降低了5%,但是再按KEY2將不再降低,說(shuō)明“內(nèi)存泄露”了。這就是前面提到的對(duì)一個(gè)指針多次申請(qǐng)內(nèi)存,而之前申請(qǐng)的內(nèi)存又沒(méi)釋放,導(dǎo)致的“內(nèi)存泄露”。
按KEY_UP按鍵,可以切換當(dāng)前操作內(nèi)存(內(nèi)部?jī)?nèi)存/外部?jī)?nèi)存),KEY1鍵用于更新p的內(nèi)容,更新后的內(nèi)容將重新顯示在LCD模塊上面。
本章,我們還可以借助USMART,測(cè)試內(nèi)存的分配和釋放,有興趣的朋友可以動(dòng)手試試。如右圖USMART測(cè)試內(nèi)存管理函數(shù)所示。
/////////////////////////插補(bǔ):printf和sprintf函數(shù)的用法////////////////////////////
printf和sprintf函數(shù)的用法非常重要,用于程序參數(shù)調(diào)試。這兩個(gè)函數(shù)都包含在系統(tǒng)啟動(dòng)代碼“stdio.h”頭文件中;
1,例:printf("Memory Malloc Test%03d\n",i);//將“Memory Malloc Test”用串口輸出,利用串口助手可以看到輸出的結(jié)果;
"03"表示參數(shù)“i”的值只顯示3位,%d-輸出控制符;d-將“i”以十進(jìn)制的形式輸出;i的范圍0--255(因?yàn)槭莡8類(lèi)型);輸出參數(shù)可以是多個(gè),可以參考郝斌老師的相關(guān)視頻;輸出控制符包含:%Ld--L代表long類(lèi)型;%c--代表字符類(lèi)型;:%X--代表16進(jìn)制并大寫(xiě);%s-字符串類(lèi)型
2,如何理解字符串打印函數(shù)int sprintf(char * __restrict /*s*/, const char * __restrict /*format*/, ...) __attribute__((__nonnull__(1,2)));?
在內(nèi)存管理實(shí)驗(yàn)中例如,sprintf((char*)p,"Memory Malloc Test%03d",i)函數(shù)的使用問(wèn)題:
1),第一個(gè)形參(char*)p的意思是(第一個(gè)形參必須是指針類(lèi)型),第二個(gè)形參即字符串“Memory Malloc Test%03d”存儲(chǔ)在內(nèi)存中的具體指針值,因?yàn)樽址莡8類(lèi)型即char*類(lèi)型,所以“(char*)p”與之呼應(yīng);因?yàn)榈诙䝼(gè)形參“Memory Malloc Test%03d”中有輸出控制符“%03d”,所以第一個(gè)形參(char*)p的值是變化的(因?yàn)閰?shù)“i”的值在變);這里輸出控制符“%03d”的意思可以參考printf()函數(shù);
也就是說(shuō),sprintf函數(shù)的第一個(gè)形參必須是指針類(lèi)型,它是第二個(gè)形參(輸出內(nèi)容)在存儲(chǔ)器中存儲(chǔ)的首地址,是一個(gè)指針變量,第三個(gè)形參就是要輸出的參數(shù);所以說(shuō)sprintf函數(shù)包含的內(nèi)容很多,作用很大。
2),sprintf函數(shù)的作用是在顯示屏中顯示相關(guān)參數(shù),即向p寫(xiě)入一些內(nèi)容即Memory Malloc Test%03d”;
結(jié)合LCD_ShowString(60,270,200,16,16,p)的顯示結(jié)果更好理解,即顯示P的存儲(chǔ)內(nèi)容即在相應(yīng)的坐標(biāo)處“Memory Malloc Test%03d”;”
3),例子:
u8 s[8];
char* who = "I"; //將字符“I”賦給char* 類(lèi)型變量who;
char* whom = "STM32"; //將字符串“STM32”賦給char* 類(lèi)型變量whom;
sprintf(s, "%s love %s.", who, whom); //產(chǎn)生:"I love STM32. " 這字符串寫(xiě)到s中
LCD_ShowString(60,250,200,16,16,s);
//sprintf(s, "%10.3f", 3.1415626); //產(chǎn)生:" 3.142",浮點(diǎn)型顯示
4),sprintf函數(shù)一般情況下是用在需要字符顯示的場(chǎng)合,比如你要顯示一個(gè)數(shù)字,通常的做法是取出某一位然后加上0x30這個(gè)數(shù),這樣一位一位來(lái)比較麻煩,用sprintf這個(gè)函數(shù)呢,一次性就給你搞定了
比如你想打印3.1415926這個(gè)數(shù)值到液晶上顯示,通常的做法代碼就很多而且亂,有了這個(gè)函數(shù)呢,直接這樣
float PI=3.1415926;
u16 strbuffer[10];
sprintf(strbuffer,"PI=:%09d",PI);
然后直接將strbuffer這個(gè)數(shù)組送去顯示即可,或者打印到串口,這樣就可以直接字符顯示了
注意:sprintf函數(shù)必須結(jié)合LCD顯示函數(shù)使用才能有效!并且形參必須定義好合適的數(shù)據(jù)類(lèi)型;sprintf()函數(shù)的最大作用就是非常方便的在LCD顯示屏上顯示自己想要的數(shù)據(jù)類(lèi)型!參考關(guān)于sprintf函數(shù)的實(shí)驗(yàn)。
3,疑問(wèn)?
a,在51單片機(jī)中,如何將sprintf函數(shù)包含進(jìn)51的啟動(dòng)代碼中?如果不將sprintf函數(shù)包含進(jìn)51的頭文件,顯示屏肯定不能用sprintf函數(shù)顯示數(shù)據(jù)。
b,在stdio.h中,找到的是int sprintf(char * __restrict /*s*/, const char * __restrict /*format*/, ...) __attribute__((__nonnull__(1,2)));怎么看不到函數(shù)內(nèi)容?
sprintf是C語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供的函數(shù), 包含在stdio.h中, 只要在文件頭#include <stdio.h>即可.
原型為int sprintf ( char * str, const char * format, ... );
/////////////////////////插補(bǔ):printf和sprintf函數(shù)的用法////////////////////////////