www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 嵌入式 > 嵌入式大雜燴
[導讀]點擊上方「嵌入式大雜燴」,選擇「置頂公眾號」第一時間查看嵌入式筆記!來源:CSDN01.調試相關的宏在Linux使用gcc編譯程序的時候,對于調試的語句還具有一些特殊的語法。gcc編譯的過程中,會生成一些宏,可以使用這些宏分別打印當前源文件的信息,主要內容是當前的文件、當前運行的...

點擊上方「嵌入式大雜燴」,選擇「置頂公眾號」第一時間查看嵌入式筆記!

來源:CSDN

01.調試相關的宏

在Linux使用gcc編譯程序的時候,對于調試的語句還具有一些特殊的語法。

gcc編譯的過程中,會生成一些宏,可以使用這些宏分別打印當前源文件的信息,主要內容是當前的文件、當前運行的函數和當前的程序行。

具體宏如下:

__FILE__??當前程序源文件?(char*)
__FUNCTION__??當前運行的函數?(char*)
__LINE__??當前的函數行?(int)
這些宏不是程序代碼定義的,而是有編譯器產生的。這些信息都是在編譯器處理文件的時候動態(tài)產生的。

「測試示例:」

#include?

int?main(void)
{
????printf("file:?%s\n",?__FILE__);
????printf("function:?%s\n",?__FUNCTION__);
????printf("line:?%d\n",?__LINE__);

????return?0;
}

02.# 字符串化操作符

在gcc的編譯系統(tǒng)中,可以使用#將當前的內容轉換成字符串。

「程序示例:」

#include?

#define?DPRINT(expr)?printf("
%s?=?%d\n",?#expr,?expr);

int?main(void)
{
????int?x?=?3;
????int?y?=?5;

????DPRINT(x?/?y);
????DPRINT(x? ?y);
????DPRINT(x?*?y);
????
????return?0;
}
「執(zhí)行結果:」

deng@itcast:~/tmp$?gcc?test.c?
deng@itcast:~/tmp$?./a.out??
x?/?y?=?0
x? ?y?=?8
x?*?y?=?15
#expr表示根據宏中的參數(即表達式的內容),生成一個字符串。該過程同樣是有編譯器產生的,編譯器在編譯源文件的時候,如果遇到了類似的宏,會自動根據程序中表達式的內容,生成一個字符串的宏。

這種方式的優(yōu)點是可以用統(tǒng)一的方法打印表達式的內容,在程序的調試過程中可以方便直觀的看到轉換字符串之后的表達式。

具體的表達式的內容是什么,有編譯器自動寫入程序中,這樣使用相同的宏打印所有表達式的字符串。

//打印字符
#define?debugc(expr)?printf("?%s?=?%c\n",?#expr,?expr)
//打印浮點數
#define?debugf(expr)?printf("?%s?=?%f\n",?#expr,?expr)
//按照16進制打印整數
#define?debugx(expr)?printf("?%s?=?0X%x\n",?#expr,?expr);
由于#expr本質上市一個表示字符串的宏,因此在程序中也可以不適用%s打印它的內容,而是可以將其直接與其它的字符串連接。

因此,上述宏可以等價以下形式:

//打印字符
#define?debugc(expr)?printf("?#expr?=?%c\n",?expr)
//打印浮點數
#define?debugf(expr)?printf("?#expr?=?%f\n",?expr)
//按照16進制打印整數
#define?debugx(expr)?printf("?#expr?=?0X%x\n",?expr);
「總結:」

#是C語言預處理階段的字符串化操作符,可將宏中的內容轉換成字符串。

03.## 連接操作符

在gcc的編譯系統(tǒng)中,##是C語言中的連接操作符,可以在編譯的預處理階段實現字符串連接的操作。

「程序示例:」

#include?

#define?test(x)?test##x

void?test1(int?a)
{
????printf("test1?a?=?%d\n",?a);
}

void?test2(char?*s)
{
????printf("test2?s?=?%s\n",?s);
}

int?main(void)
{
????test(1)(100);

????test(2)("hello?world");
????
????return?0;
}
上述程序中,test(x)宏被定義為test##x, 他表示test字符串和x字符串的連接。

在程序的調試語句中,##常用的方式如下

#define?DEBUG(fmt,?args...)?printf(fmt,?##args)
替換的方式是將參數的兩個部分以##連接。##表示連接變量代表前面的參數列表。使用這種形式可以將宏的參數傳遞給一個參數。args…是宏的參數,表示可變的參數列表,使用##args將其傳給printf函數.

「總結:」

##是C語言預處理階段的連接操作符,可實現宏參數的連接。

04.調試宏第一種形式

一種定義的方式:

#define?DEBUG(fmt,?args...)?????????????\
????{???????????????????????????????????\
????printf("file:%s?function:?%s?line:?%d?",?__FILE__,?__FUNCTION__,?__LINE__);\
????printf(fmt,?##args);????????????????\
????}

「程序示例:」

#include?

#define?DEBUG(fmt,?args...)?????????????\
????{???????????????????????????????????\
????printf("file:%s?function:?%s?line:?%d?",?__FILE__,?__FUNCTION__,?__LINE__);\
????printf(fmt,?##args);????????????????\
????}



int?main(void)
{
????int?a?=?100;
????int?b?=?200;

????char?*s?=?"hello?world";
????DEBUG("a?=?%d?b?=?%d\n",?a,?b);
????DEBUG("a?=?%x?b?=?%x\n",?a,?b);
????DEBUG("s?=?%s\n",?s);
????
????return?0;
}
「總結:」

上面的DEBUG定義的方式是兩條語句的組合,不可能在產生返回值,因此不能使用它的返回值。

05.調試宏的第二種定義方式

調試宏的第二種定義方式

#define?DEBUG(fmt,?args...)?????????????\
????printf("file:%s?function:?%s?line:?%d?"fmt,?\
????__FILE__,?__FUNCTION__,?__LINE__,?##args)

程序示例

#include?

#define?DEBUG(fmt,?args...)?????????????\
????printf("file:%s?function:?%s?line:?%d?"fmt,?\
????__FILE__,?__FUNCTION__,?__LINE__,?##args)



int?main(void)
{
????int?a?=?100;
????int?b?=?200;

????char?*s?=?"hello?world";
????DEBUG("a?=?%d?b?=?%d\n",?a,?b);
????DEBUG("a?=?%x?b?=?%x\n",?a,?b);
????DEBUG("s?=?%s\n",?s);
????
????return?0;
}
「總結:」

fmt必須是一個字符串,不能使用指針,只有這樣才可以實現字符串的功能。

06.對調試語句進行分級審查

即使定義了調試的宏,在工程足夠大的情況下,也會導致在打開宏開關的時候在終端出現大量的信息。而無法區(qū)分哪些是有用的。

這個時候就要加入分級檢查機制,可以定義不同的調試級別,這樣就可以對不同重要程序和不同的模塊進行區(qū)分,需要調試哪一個模塊就可以打開那一個模塊的調試級別。

一般可以利用配置文件的方式顯示,其實Linux內核也是這么做的,它把調試的等級分成了7個不同重要程度的級別,只有設定某個級別可以顯示,對應的調試信息才會打印到終端上。

可以寫出一下配置文件

[debug]
debug_level=XXX_MODULE
解析配置文件使用標準的字符串操作庫函數就可以獲取XXX_MODULE這個數值。

int?show_debug(int?level)
{
????if?(level?==?XXX_MODULE)
????{
????????#define?DEBUG(fmt,?args...)?????????????\
????????printf("file:%s?function:?%s?line:?%d?"fmt,?\
????????__FILE__,?__FUNCTION__,?__LINE__,?##args)???????

????}
????else?if?(...)
????{
????????....
????}
}

07.條件編譯調試語句

在實際的開發(fā)中,一般會維護兩種源程序,一種是帶有調試語句的調試版本程序,另外一種是不帶有調試語句的發(fā)布版本程序。

然后根據不同的條件編譯選項,編譯出不同的調試版本和發(fā)布版本的程序。

在實現過程中,可以使用一個調試宏來控制調試語句的開關。

#ifdef?USE_DEBUG
????????#define?DEBUG(fmt,?args...)?????????????\
????????printf("file:%s?function:?%s?line:?%d?"fmt,?\
????????__FILE__,?__FUNCTION__,?__LINE__,?##args)??

#else
??#define?DEBUG(fmt,?args...)

#endif
如果USE_DEBUG被定義,那么有調試信息,否則DEBUG就為空。

如果需要調試信息,就只需要在程序中更改一行就可以了。

#define?USE_DEBUG
#undef?USE_DEBUG
定義條件編譯的方式使用一個帶有值的宏

#if?USE_DEBUG
????????#define?DEBUG(fmt,?args...)?????????????\
????????printf("file:%s?function:?%s?line:?%d?"fmt,?\
????????__FILE__,?__FUNCTION__,?__LINE__,?##args)??

#else
??#define?DEBUG(fmt,?args...)

#endif
可以使用如下方式進行條件編譯

#ifndef?USE_DEBUG
#define?USE_DEBUG?0
#endif

08.使用do…while的宏定義

使用宏定義可以將一些較為短小的功能封裝,方便使用。宏的形式和函數類似,但是可以節(jié)省函數跳轉的開銷。

如何將一個語句封裝成一個宏,在程序中常常使用do…while(0)的形式。

#define?HELLO(str)?do?{?\
printf("hello:?%s\n",?str);?\
}while(0)

「程序示例:」

int?cond?=?1;
if?(cond)
????HELLO("true");
else
????HELLO("false");

09.代碼剖析

對于比較大的程序,可以借助一些工具來首先把需要優(yōu)化的點清理出來。接下來我們來看看在程序執(zhí)行過程中獲取數據并進行分析的工具:代碼剖析程序。

「測試程序:」

#include?


#define?T?100000

void?call_one()
{
????int?count?=?T?*?1000;
????while(count--);
}

void?call_two()
{
????int?count?=?T?*?50;
????while(count--);
}

void?call_three()
{
????int?count?=?T?*?20;
????while(count--);
}


int?main(void)
{
????int?time?=?10;

????while(time--)
????{
????????call_one();
????????call_two();
????????call_three();
????}
????
????return?0;
}
編譯的時候加入-pg選項:

deng@itcast:~/tmp$?gcc?-pg??test.c?-o?test
執(zhí)行完成后,在當前文件中生成了一個gmon.out文件。

deng@itcast:~/tmp$?./test??
deng@itcast:~/tmp$?ls
gmon.out??test??test.c
deng@itcast:~/tmp$?
「使用gprof剖析主程序:」

deng@itcast:~/tmp$?gprof?test
Flat?profile:

Each?sample?counts?as?0.01?seconds.
??%???cumulative???self??????????????self?????total???????????
?time???seconds???seconds????calls??ms/call??ms/call??name????
?95.64??????1.61?????1.61???????10???160.68???160.68??call_one
??3.63??????1.67?????0.06???????10?????6.10?????6.10??call_two
??2.42??????1.71?????0.04???????10?????4.07?????4.07??call_three
其中主要的信息有兩個,一個是每個函數執(zhí)行的時間占程序總時間的百分比,另外一個就是函數被調用的次數。通過這些信息,可以優(yōu)化核心程序的實現方式來提高效率。

當然這個剖析程序由于它自身特性有一些限制,比較適用于運行時間比較長的程序,因為統(tǒng)計的時間是基于間隔計數這種機制,所以還需要考慮函數執(zhí)行的相對時間,如果程序執(zhí)行時間過短,那得到的信息是沒有任何參考意義的。

「將上訴程序時間縮短:」

#include?


#define?T?100

void?call_one()
{
????int?count?=?T?*?1000;
????while(count--);
}

void?call_two()
{
????int?count?=?T?*?50;
????while(count--);
}

void?call_three()
{
????int?count?=?T?*?20;
????while(count--);
}


int?main(void)
{
????int?time?=?10;

????while(time--)
????{
????????call_one();
????????call_two();
????????call_three();
????}
????
????return?0;
}
「剖析結果如下:」

deng@itcast:~/tmp$?gcc?-pg?test.c?-o?test
deng@itcast:~/tmp$?./test??
deng@itcast:~/tmp$?gprof?test
Flat?profile:

Each?sample?counts?as?0.01?seconds.
?no?time?accumulated

??%???cumulative???self??????????????self?????total???????????
?time???seconds???seconds????calls??Ts/call??Ts/call??name????
??0.00??????0.00?????0.00???????10?????0.00?????0.00??call_one
??0.00??????0.00?????0.00???????10?????0.00?????0.00??call_three
??0.00??????0.00?????0.00???????10?????0.00?????0.00??call_two
因此該剖析程序對于越復雜、執(zhí)行時間越長的函數也適用。

那么是不是每個函數執(zhí)行的絕對時間越長,剖析顯示的時間就真的越長呢?可以再看如下的例子

#include?


#define?T?100

void?call_one()
{
????int?count?=?T?*?1000;
????while(count--);
}

void?call_two()
{
????int?count?=?T?*?100000;
????while(count--);
}

void?call_three()
{
????int?count?=?T?*?20;
????while(count--);
}


int?main(void)
{
????int?time?=?10;

????while(time--)
????{
????????call_one();
????????call_two();
????????call_three();
????}
????
????return?0;
}
「剖析結果如下:」

deng@itcast:~/tmp$?gcc?-pg?test.c?-o?test
deng@itcast:~/tmp$?./test??
deng@itcast:~/tmp$?gprof?test
Flat?profile:

Each?sample?counts?as?0.01?seconds.
??%???cumulative???self??????????????self?????total???????????
?time???seconds???seconds????calls??ms/call??ms/call??name????
101.69??????0.15?????0.15???????10????15.25????15.25??call_two
??0.00??????0.15?????0.00???????10?????0.00?????0.00??call_one
??0.00??????0.15?????0.00???????10?????0.00?????0.00??call_three

「總結:」

在使用gprof工具的時候,對于一個函數進行gprof方式的剖析,實質上的時間是指除去庫函數調用和系統(tǒng)調用之外,純碎應用部分開發(fā)的實際代碼運行的時間,也就是說time一項描述的時間值不包括庫函數printf、系統(tǒng)調用system等運行的時間。

這些實用庫函數的程序雖然運行的時候將比最初的程序實用更多的時間,但是對于剖析函數來說并沒有影響。

10.附錄

嵌入式Linux上的C語言編程實踐

來源:https://blog.csdn.net/dengjin20104042056

本文來源網絡,版權歸原作者所有。如涉及作品版權問題,請聯(lián)系我進行刪除。

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅動電源的性能直接關系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅動性能的關鍵。

關鍵字: 工業(yè)電機 驅動電源

LED 驅動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅動電源 照明系統(tǒng) 散熱

根據LED驅動電源的公式,電感內電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅動系統(tǒng)中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅動電源

在現代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質量和效率直接關系到城市的公共安全、居民生活質量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅動電源 功率因數校正

在LED照明技術日益普及的今天,LED驅動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現在的LED驅動電源

關鍵字: LED 驅動電源 開關電源

LED驅動電源是把電源供應轉換為特定的電壓電流以驅動LED發(fā)光的電壓轉換器,通常情況下:LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅動電源
關閉