




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第第頁嵌入式軟件項目中的一些常用套路與技巧01.調(diào)試相關(guān)的宏
在(Linux)使用gcc編譯程序的時候,對于調(diào)試的語句還具有一些特殊的語法。
gcc編譯的過程中,會生成一些宏,可以使用這些宏分別打印當前源文件的信息,主要內(nèi)容是當前的文件、當前運行的函數(shù)和當前的程序行。
具體宏如下:
__FILE__
當前程序源文件
(char*)__FUNC(TI)ON__
當前運行的函數(shù)
(char*)__LINE__
當前的函數(shù)行
(int)
這些宏不是(程序代碼)定義的,而是有編譯器產(chǎn)生的。這些信息都是在編譯器處理文件的時候動態(tài)產(chǎn)生的。
「測試示例:」
#include
int
main(void){
printf("file:
%s",
__FILE__);
printf("function:
%s",
__FUNCTION__);
printf("line:
%d",
__LINE__);
return
0;}
02.#字符串化操作符
在gcc的編譯系統(tǒng)中,可以使用#將當前的內(nèi)容轉(zhuǎn)換成字符串。
「程序示例:」
#include
#define
DPRINT(expr)
printf("%s
=
%d",
#expr,
expr);int
main(void){
int
x
=
3;
int
y
=
5;
DPRINT(x
/
y);
DPRINT(x
+
y);
DPRINT(x
*
y);
return
0;}
「執(zhí)行結(jié)果:」
deng@itcast:~/tmp$
gcc
(te)st.c
deng@itcast:~/tmp$
./a.out
x
/
y
=
0x
+
y
=
8x
*
y
=
15
#expr表示根據(jù)宏中的參數(shù)(即表達式的內(nèi)容),生成一個字符串。該過程同樣是有編譯器產(chǎn)生的,編譯器在編譯源文件的時候,如果遇到了類似的宏,會自動根據(jù)程序中表達式的內(nèi)容,生成一個字符串的宏。
這種方式的優(yōu)點是可以用統(tǒng)一的方法打印表達式的內(nèi)容,在程序的調(diào)試過程中可以方便直觀的看到轉(zhuǎn)換字符串之后的表達式。
具體的表達式的內(nèi)容是什么,有編譯器自動寫入程序中,這樣使用相同的宏打印所有表達式的字符串。
//打印字符#define
debugc(expr)
printf("
%s
=
%c",
#expr,
expr)//打印浮點數(shù)#define
debugf(expr)
printf("
%s
=
%f",
#expr,
expr)//按照16進制打印整數(shù)#define
debugx(expr)
printf("
%s
=
0X%x",
#expr,
expr);
由于#expr本質(zhì)上市一個表示字符串的宏,因此在程序中也可以不適用%s打印它的內(nèi)容,而是可以將其直接與其它的字符串連接。
因此,上述宏可以等價以下形式:
//打印字符#define
debugc(expr)
printf("
#expr
=
%c",
expr)//打印浮點數(shù)#define
debugf(expr)
printf("
#expr
=
%f",
expr)//按照16進制打印整數(shù)#define
debugx(expr)
printf("
#expr
=
0X%x",
expr);
「總結(jié):」
#是(C語言)預處理階段的字符串化操作符,可將宏中的內(nèi)容轉(zhuǎn)換成字符串。
03.##連接操作符
在gcc的編譯系統(tǒng)中,##是C語言中的連接操作符,可以在編譯的預處理階段實現(xiàn)字符串連接的操作。
「程序示例:」
#include
#define
test(x)
test##xvoid
test1(int
a){
printf("test1
a
=
%d",
a);}void
test2(char
*s){
printf("test2
s
=
%s",
s);}int
main(void){
test(1)(100);
test(2)("hello
world");
return
0;}
上述程序中,test(x)宏被定義為test##x,他表示test字符串和x字符串的連接。
在程序的調(diào)試語句中,##常用的方式如下
#define
DEBUG(fmt,
args...)
printf(fmt,
##args)
替換的方式是將參數(shù)的兩個部分以##連接。##表示連接變量代表前面的參數(shù)列表。使用這種形式可以將宏的參數(shù)傳遞給一個參數(shù)。args…是宏的參數(shù),表示可變的參數(shù)列表,使用##args將其傳給printf函數(shù).
「總結(jié):」
##是C語言預處理階段的連接操作符,可實現(xiàn)宏參數(shù)的連接。
04.調(diào)試宏第一種形式
一種定義的方式:
#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",
a,
b);
DEBUG("a
=
%x
b
=
%x",
a,
b);
DEBUG("s
=
%s",
s);
return
0;}
「總結(jié):」
上面的DEBUG定義的方式是兩條語句的組合,不可能在產(chǎn)生返回值,因此不能使用它的返回值。
05.調(diào)試宏的第二種定義方式
調(diào)試宏的第二種定義方式
#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",
a,
b);
DEBUG("a
=
%x
b
=
%x",
a,
b);
DEBUG("s
=
%s",
s);
return
0;}
「總結(jié):」
fmt必須是一個字符串,不能使用指針,只有這樣才可以實現(xiàn)字符串的功能。
06.對調(diào)試語句進行分級審查
即使定義了調(diào)試的宏,在工程足夠大的情況下,也會導致在打開宏開關(guān)的時候在終端出現(xiàn)大量的信息。而無法區(qū)分哪些是有用的。
這個時候就要加入分級檢查機制,可以定義不同的調(diào)試級別,這樣就可以對不同重要程序和不同的模塊進行區(qū)分,需要調(diào)試哪一個模塊就可以打開那一個模塊的調(diào)試級別。
一般可以利用配置文件的方式顯示,其實Linux內(nèi)核也是這么做的,它把調(diào)試的等級分成了7個不同重要程度的級別,只有設(shè)定某個級別可以顯示,對應的調(diào)試信息才會打印到終端上。
可以寫出一下配置文件
[debug]debug_level=XXX_MODULE
解析配置文件使用標準的字符串操作庫函數(shù)就可以獲取XXX_MODULE這個數(shù)值。
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.條件編譯調(diào)試語句
在實際的開發(fā)中,一般會維護兩種源程序,一種是帶有調(diào)試語句的調(diào)試版本程序,另外一種是不帶有調(diào)試語句的發(fā)布版本程序。
然后根據(jù)不同的條件編譯選項,編譯出不同的調(diào)試版本和發(fā)布版本的程序。
在實現(xiàn)過程中,可以使用一個調(diào)試宏來控制調(diào)試語句的開關(guān)。
#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被定義,那么有調(diào)試信息,否則DEBUG就為空。
如果需要調(diào)試信息,就只需要在程序中更改一行就可以了。
#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的宏定義
使用宏定義可以將一些較為短小的功能封裝,方便使用。宏的形式和函數(shù)類似,但是可以節(jié)省函數(shù)跳轉(zhuǎn)的開銷。
如何將一個語句封裝成一個宏,在程序中常常使用do…while(0)的形式。
#define
HELLO(str)
do
{
printf("hello:
%s",
str);
}while(0)
「程序示例:」
int
cond
=
1;if
(cond)
HELLO("true");else
HELLO("false");
09.代碼剖析
對于比較大的程序,可以借助一些工具來首先把需要優(yōu)化的點清理出來。接下來我們來看看在程序執(zhí)行過程中獲取數(shù)據(jù)并進行分析的工具:代碼剖析程序。
「測試程序:」
#include
#define
T
100000void
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$
lsgmon.out
test
test.cdeng@itcast:~/tmp$
「使用gprof剖析主程序:」
deng@itcast:~/tmp$
gprof
testFlat
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
其中主要的信息有兩個,一個是每個函數(shù)執(zhí)行的時間占程序總時間的百分比,另外一個就是函數(shù)被調(diào)用的次數(shù)。通過這些信息,可以優(yōu)化核心程序的實現(xiàn)方式來提高效率。
當然這個剖析程序由于它自身特性有一些限制,比較適用于運行時間比較長的程序,因為統(tǒng)計的時間是基于間隔計數(shù)這種機制,所以還需要考慮函數(shù)執(zhí)行的相對時間,如果程序執(zhí)行時間過短,那得到的信息是沒有任何參考意義的。
「將上訴程序時間縮短:」
#include
#define
T
100void
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;}
「剖析結(jié)果如下:」
deng@itcast:~/tmp$
gcc
-pg
test.c
-o
testdeng@itcast:~/tmp$
./test
deng@itcast:~/tmp$
gprof
testFlat
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
因此該剖析程序?qū)τ谠綇碗s、執(zhí)行時間越長的函數(shù)也適用。
那么是不是每個函數(shù)執(zhí)行的絕對時間越長,剖析顯示的時間就真的越長呢?可以再看如下的例子
#include
#define
T
100void
call_one(){
int
count
=
T
*
1000;
while(count--);}void
call_two(){
int
count
=
T
*
100000;
while(count--);}void
call_three(){
int
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 科研儀器物流承攬合同模板
- 2025年中空纖維分離膜項目建議書
- 高新技術(shù)企業(yè)貸款居間合同
- 學術(shù)會議策劃服務(wù)行業(yè)跨境出海戰(zhàn)略研究報告
- 2025江西省建筑安全員A證考試題庫附答案
- 2025天津市建筑安全員-A證考試題庫附答案
- 2025建筑安全員考試題庫及答案
- 消防工程設(shè)計施工合同
- 2025青海省安全員《A證》考試題庫
- 軟件安全測試服務(wù)合作協(xié)議
- (兒科學課件)腎病綜合征
- 光纜線路工程段終版施工圖
- 2023年最新的郭氏宗祠的對聯(lián)大全
- 礦井年度災害預防和處理計劃
- 畢業(yè)論文-基于Java Web的模擬駕??荚囅到y(tǒng)設(shè)計與實現(xiàn)
- 駱駝祥子1一24章批注
- 《中國古代文學史》宋代文學完整教學課件
- 新部編人教版四年級下冊道德與法治全冊教案(教學設(shè)計)
- 物業(yè)服務(wù)企業(yè)市場拓展戰(zhàn)略規(guī)劃課件
- 進制轉(zhuǎn)換教學設(shè)計
- 垂直度和跳動形位公差間的關(guān)系及取代應用下
評論
0/150
提交評論