C語(yǔ)言宏定義實(shí)現(xiàn)極似面向?qū)ο笳Z(yǔ)法,含偽gc
本文之前在百度貼吧發(fā)布過(guò),基本沒(méi)人看,在這試試水。
原文鏈接:
http://tieba.baidu.com/p/7337300220?share=9105&fr=share&see_lz=1&sfc=copy&client_type=2&client_version=11.4.8.0&st=1620397940&unique=D2CF6E36E7E60F7667D849A1F58E0175?
最近在學(xué)m3,m4單片機(jī),都是C語(yǔ)言編程,打了兩年的《JVAV》一接觸C語(yǔ)言編程環(huán)境十分不習(xí)慣,打算用C封裝一個(gè)面向?qū)ο蠡静僮鳌?/p>
目前通過(guò)宏定義實(shí)現(xiàn)了對(duì)象的內(nèi)存分配,this指針的切換,構(gòu)造函數(shù)的執(zhí)行等基本功能。
每個(gè)類(結(jié)構(gòu)體)都可以通過(guò)包含一個(gè)通用結(jié)構(gòu)體的方式來(lái)實(shí)現(xiàn)繼承。
由于水平比較菜,gc實(shí)現(xiàn)了一個(gè)最菜的計(jì)數(shù)法回收內(nèi)存,內(nèi)存不夠用的時(shí)候根據(jù)計(jì)數(shù)來(lái)回收不常用的對(duì)象以及該對(duì)象動(dòng)態(tài)分配的數(shù)據(jù)。在單片機(jī)環(huán)境中運(yùn)行影響不大,單片機(jī)任務(wù)注重實(shí)時(shí)性,長(zhǎng)時(shí)間不用的內(nèi)存回收兼容該場(chǎng)景。
先看看運(yùn)行效果和main函數(shù)代碼:


具體實(shí)現(xiàn):
首先是對(duì)象內(nèi)存分配宏定義new()的實(shí)現(xiàn)
我定義了兩種
一種是帶參數(shù)的new_args(type,args),相當(dāng)于帶參數(shù)的構(gòu)造函數(shù)
一種是不帶參數(shù)的new(type),相當(dāng)于無(wú)參構(gòu)造函數(shù)

new實(shí)現(xiàn)了之后就是類的結(jié)構(gòu)了,一個(gè)類(結(jié)構(gòu)體)中除了你自己要用的函數(shù)指針和屬性之外,必須額外包括:無(wú)參構(gòu)造函數(shù)和有參構(gòu)造函數(shù)以及計(jì)數(shù)器。
而且由于語(yǔ)法限制,類構(gòu)造函數(shù)名字必須按照如下格式:
例如你的類叫TEST,那構(gòu)造函數(shù)就要叫
void Const_TEST_ARGS(TEST obj,void* str,...);
void Const_TEST(TEST obj);
typedef出來(lái)的結(jié)構(gòu)體名字也要按照如下格式:
例如你的類叫TEST,typedef結(jié)構(gòu)體就要叫_TEST
下面放一個(gè)TEST的模板和一個(gè)List的頭文件參考參考:


最后就是gc了,這塊折磨了我億小會(huì),由于比較菜不知道realloc重新分配會(huì)修改之前的地址導(dǎo)致程序一直 -1073740940
先說(shuō)下基本思路:
1.用一個(gè)List存儲(chǔ)new出來(lái)的對(duì)象
2.再用一個(gè)List存儲(chǔ)這些對(duì)象創(chuàng)建的動(dòng)態(tài)內(nèi)存空間。
3.寫(xiě)一個(gè)通用結(jié)構(gòu)體,里面就放一個(gè)int型的計(jì)數(shù)值,通過(guò)對(duì)象強(qiáng)轉(zhuǎn)從而得到該對(duì)象的計(jì)數(shù)值
4.采用最簡(jiǎn)單的計(jì)數(shù)法實(shí)現(xiàn)對(duì)象回收,長(zhǎng)時(shí)間不調(diào)用的話到點(diǎn)就會(huì)執(zhí)行回收,并free掉對(duì)象指針以及該對(duì)象分配的動(dòng)態(tài)內(nèi)存空間。
5.這里長(zhǎng)時(shí)間不調(diào)用會(huì)被回收,那么什么是調(diào)用呢?
#define list $(LLL)
#define test $(TTT)
TEST TTT=new(TEST);
List LLL=new(List);
只要使用了list.和test.就算是調(diào)用,計(jì)數(shù)器就會(huì)清掉,并且把this指針切換成當(dāng)前對(duì)象
或者你可以這樣調(diào)用$(LLL).和$(TTT)效果是一樣的。
上個(gè)圖:
首先是gc.h


結(jié)構(gòu)很簡(jiǎn)單,兩個(gè)List一個(gè)用來(lái)裝new出來(lái)的對(duì)象,一個(gè)用來(lái)裝這些對(duì)象產(chǎn)生的數(shù)據(jù)
_Data這個(gè)結(jié)構(gòu)體中的Belonger用來(lái)存儲(chǔ)數(shù)據(jù)的產(chǎn)生者(是哪個(gè)對(duì)象創(chuàng)建該數(shù)據(jù)的),Value用來(lái)存儲(chǔ)數(shù)據(jù)指針。到時(shí)候刪除對(duì)象的時(shí)候,把Belonger和該對(duì)象相同的數(shù)據(jù)也刪了。
然后是函數(shù)實(shí)現(xiàn):

gc_Init()函數(shù)中初始化了兩個(gè)List并開(kāi)啟了線程用于執(zhí)行g(shù)c
gc_Run()里面不斷判斷每個(gè)對(duì)象的計(jì)數(shù)值,超過(guò)了閾值就清掉該對(duì)象和對(duì)應(yīng)的數(shù)據(jù)
gc_Clear()提供給用戶主動(dòng)調(diào)用,直接清掉你不要的對(duì)象
gc_Run()里面不斷遍歷對(duì)象List,通過(guò)把List中的對(duì)象強(qiáng)轉(zhuǎn)成通用結(jié)構(gòu)體Template來(lái)獲取該對(duì)象的計(jì)數(shù)值,如果超過(guò)了閾值,就清掉該對(duì)象,然后去遍歷數(shù)據(jù)List,通過(guò)Belonger判斷該對(duì)象是否創(chuàng)建了數(shù)據(jù),如果有,就把其創(chuàng)建的數(shù)據(jù)也清掉。
邏輯比較簡(jiǎn)單,可以正常實(shí)現(xiàn)基本功能,基本滿足單片機(jī)任務(wù)中的對(duì)象回收需求。
最后放個(gè)List類里面有關(guān)gc的代碼
擴(kuò)容時(shí):

縮容時(shí):

刪除數(shù)據(jù):

當(dāng)然這代碼寫(xiě)出來(lái)了肯定是有bug的,水平菜情有可原
宏定義BUG,及其影響手感,目前想不到解決方案
#define list $(LLL)
#define test $(TTT)
TEST TTT=new(TEST);
List LLL=new(List);
使用TTT.a=100;或者TTT.b=10;時(shí)會(huì)報(bào)錯(cuò),因?yàn)槲液甓x采用的是表達(dá)式復(fù)合語(yǔ)句,終究還是表達(dá)式,表達(dá)式眾所周知是不可以被賦值的。
如果使用另一種宏定義方法:
#define V(n) this=n;\
?n->counter=0;\
?(*n)
照樣會(huì)有bug,bug還比剛才的嚴(yán)重更多,使用這款宏定義后,賦值可以了,但是使用帶返回值的函數(shù)就會(huì)出錯(cuò),要改也可以,但是代價(jià)很大,必須把所有的函數(shù)都加上返回值。
代碼我直接發(fā)出來(lái),帶師門(mén)幫忙看看
[有效] https://pan.baidu.com/s/1HlZiw0c4vdIunx6GXUPT8g?
提取碼
ALYA
如果這里瀏覽量高的話,下篇我會(huì)寫(xiě)一個(gè)如何在clion上使用stm32cubemx和stm32標(biāo)準(zhǔn)庫(kù),主要是標(biāo)準(zhǔn)庫(kù),網(wǎng)上教程比較少,同樣的,我會(huì)把工程模板發(fā)出來(lái)。


可以看到成功build出了hex和bin文件