今天分享幾個嵌入式C中的實(shí)用技巧,希望能對大家有所幫助。
1、動態(tài)綁定、回調(diào)函數(shù)
回調(diào)函數(shù)可以達(dá)到動態(tài)綁定的作用,在一定程度上可以降低層與層之間的耦合。可能很多初學(xué)的小伙伴可能還不理解回調(diào)函數(shù),可以借助下圖來理解:

一般函數(shù)調(diào)用的順序都是上層函數(shù)(調(diào)用者)調(diào)用下層函數(shù)(被調(diào)用者)。而通過上圖我們可以看到下層模塊的函數(shù)2調(diào)用了上層模塊的函數(shù)3,這個調(diào)用過程與一般的調(diào)用過程相反,這個過程叫做回調(diào),這里上層模塊的函數(shù)3就是回調(diào)函數(shù)?;卣{(diào)函數(shù)的表現(xiàn)形式是函數(shù)指針。
C庫stdlib.h中帶有一個排序函數(shù):qsort函數(shù)。這個排序函數(shù)的原型為:
void?qsort(void?*base,?size_t?nitems,?size_t?size,?int?(*compar)(const?void?*,?const?void*));
base-- 指向要排序的數(shù)組的第一個元素的指針。
nitems-- 由 base 指向的數(shù)組中元素的個數(shù)。
size-- 數(shù)組中每個元素的大小,以字節(jié)為單位。
compar-- 用來比較兩個元素的函數(shù),即函數(shù)指針(回調(diào)函數(shù))。
int?compar(const?void?*p1,?const?void?*p2);
如果compar返回值小于0(< 0),那么p1所指向元素會被排在p2所指向元素的左面;
如果compar返回值等于0(= 0),那么p1所指向元素與p2所指向元素的順序不確定;
如果compar返回值大于0(> 0),那么p1所指向元素會被排在p2所指向元素的右面。
例子:

#include?#include? int?compar_int(const?void?*p1,?const?void?*p2) { ?return?(*((int*)p1)?-?*((int*)p2)); } void?test_qsort(void) { ?int?arr[5]?=?{8,?5,?10,?1,?100}; ? ?printf("排序前:"); ?for?(int?i?=?0;?i?5;?i++) ?{ ??printf("%d?",?arr[i]); ?} ? ?qsort((int*)arr,?5,?4,?compar_int); ? ?printf(" 排序后:"); ?for?(int?i?=?0;?i?5;?i++) ?{ ??printf("%d?",?arr[i]); ?} } int?main(void) { ?test_qsort(); ?return?0; }
編譯、運(yùn)行結(jié)果:

以上就是本次的分享,如有錯誤,歡迎指出,謝謝。這是第一彈,后續(xù)還會繼續(xù)分享更多實(shí)際開發(fā)中實(shí)用的編程小技巧及編程經(jīng)驗(yàn)。歡迎持續(xù)關(guān)注。本文只是盤點(diǎn)了一些實(shí)用小技巧,并不是說無論什么場景下都要這么用,還需具體問題具體分析。
2、使用宏給結(jié)構(gòu)體初始化
如果頻繁使用一個結(jié)構(gòu)體的話,使用使用宏來給結(jié)構(gòu)體進(jìn)行賦值是很方便的一種做法。
例子:
?
?
#include?#define??NEW_RECT(length,?width)??{(length),?(width)} typedef?struct?_Rect { ?int?length; ?int?width; }Rect; int?main(void) { ?Rect rect?=?NEW_RECT(10,?5); ?printf("rect length?=?%d,?width?=?%d ",?rect.length,?rect.width); ?return?0; }
編譯、運(yùn)行結(jié)果:

這種方法在RT-Thread的底層gpio驅(qū)動中也有見到:

3、結(jié)構(gòu)體內(nèi)置函數(shù)指針
我們常常構(gòu)造一些結(jié)構(gòu)體來存儲數(shù)據(jù),然后在一些函數(shù)中使用這些結(jié)構(gòu)體。下次不妨把數(shù)據(jù)與操作數(shù)據(jù)的函數(shù)綁在一起,更清晰明了。
例子:
#include?#define??NEW_RECT(length,?width)??{(calc_area),?(length),?(width)} typedef?struct?_Rect { ?int?(*calc_area)(struct _Rect?*pThis); ?int?length; ?int?width; }Rect; int?calc_area(struct _Rect?*pThis) { ?return?(pThis->length?*?pThis->width); } int?main(void) { ?Rect rect?=?NEW_RECT(10,?5); ?printf("rect length?=?%d,?width?=?%d ",?rect.length,?rect.width); ?printf("rect area?=?%d ",?rect.calc_area(&rect)); ?return?0; }
編譯、運(yùn)行結(jié)果:

4、使用do{}while(0)封裝宏
#define?DBG_PRINTF(fmt,?args...)??
do
{
????printf("<>?",?__FILE__,?__LINE__,?__FUNCTION__);
????printf(fmt,?##args);
}while(0)
5、void*
之前有介紹過void*。void*其實(shí)我們平時都有接觸過,比如:
void?*malloc(size_t?size)?; void?*memcpy(void?*destin,?void?*source,?unsigned?n); ......
void *常常用于函數(shù)地封裝比較多,當(dāng)然也有用在其它地方,比如在結(jié)構(gòu)體內(nèi)定義void*類型的私有指針方便擴(kuò)展結(jié)構(gòu)體。我們平時在封裝自己的函數(shù)時,也可以多考慮看看有沒有必要使用void*使得函數(shù)地通用性更強(qiáng)一些。
如果本文對你有幫助,不妨轉(zhuǎn)發(fā)分享,謝謝!
編輯:黃飛
?
電子發(fā)燒友App




評論