一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux使用gcc編譯程序的語(yǔ)法

科技綠洲 ? 來(lái)源:工程師進(jìn)階筆記 ? 作者:工程師進(jìn)階筆記 ? 2023-06-22 10:51 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

01. 調(diào)試相關(guān)的宏

Linux使用gcc編譯程序的時(shí)候,對(duì)于調(diào)試的語(yǔ)句還具有一些特殊的語(yǔ)法。

gcc編譯的過(guò)程中,會(huì)生成一些宏,可以使用這些宏分別打印當(dāng)前源文件的信息,主要內(nèi)容是當(dāng)前的文件、當(dāng)前運(yùn)行的函數(shù)和當(dāng)前的程序行。

具體宏如下:

__FILE__  當(dāng)前程序源文件 (char*)
__FUNCTION__  當(dāng)前運(yùn)行的函數(shù) (char*)
__LINE__  當(dāng)前的函數(shù)行 (int)

這些宏不是程序代碼定義的,而是有編譯器產(chǎn)生的。這些信息都是在編譯器處理文件的時(shí)候動(dòng)態(tài)產(chǎn)生的。

「測(cè)試示例:」

#include < stdio.h >

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

    return 0;
}

02. # 字符串化操作符

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

「程序示例:」

#include < stdio.h >

#define DPRINT(expr) printf("< main >%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í)行結(jié)果:」

deng@itcast:~/tmp$ gcc test.c 
deng@itcast:~/tmp$ ./a.out  
< main >x / y = 0
< main >x + y = 8
< main >x * y = 15

#expr表示根據(jù)宏中的參數(shù)(即表達(dá)式的內(nèi)容),生成一個(gè)字符串。該過(guò)程同樣是有編譯器產(chǎn)生的,編譯器在編譯源文件的時(shí)候,如果遇到了類(lèi)似的宏,會(huì)自動(dòng)根據(jù)程序中表達(dá)式的內(nèi)容,生成一個(gè)字符串的宏。

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

具體的表達(dá)式的內(nèi)容是什么,有編譯器自動(dòng)寫(xiě)入程序中,這樣使用相同的宏打印所有表達(dá)式的字符串。

//打印字符
#define debugc(expr) printf("< char > %s = %c\\n", #expr, expr)
//打印浮點(diǎn)數(shù)
#define debugf(expr) printf("< float > %s = %f\\n", #expr, expr)
//按照16進(jìn)制打印整數(shù)
#define debugx(expr) printf("< int > %s = 0X%x\\n", #expr, expr);

由于#expr本質(zhì)上市一個(gè)表示字符串的宏,因此在程序中也可以不適用%s打印它的內(nèi)容,而是可以將其直接與其它的字符串連接。

因此,上述宏可以等價(jià)以下形式:

//打印字符
#define debugc(expr) printf("< char > #expr = %c\\n", expr)
//打印浮點(diǎn)數(shù)
#define debugf(expr) printf("< float > #expr = %f\\n", expr)
//按照16進(jìn)制打印整數(shù)
#define debugx(expr) printf("< int > #expr = 0X%x\\n", expr);

「總結(jié):」

#是C語(yǔ)言預(yù)處理階段的字符串化操作符,可將宏中的內(nèi)容轉(zhuǎn)換成字符串。

03. ## 連接操作符

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

「程序示例:」

#include < stdio.h >

#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字符串的連接。

在程序的調(diào)試語(yǔ)句中,##常用的方式如下

#define DEBUG(fmt, args...) printf(fmt, ##args)

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

「總結(jié):」

##是C語(yǔ)言預(yù)處理階段的連接操作符,可實(shí)現(xiàn)宏參數(shù)的連接。

04. 調(diào)試宏第一種形式

一種定義的方式:

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

「程序示例:」

#include < stdio.h >

#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;
}

「總結(jié):」

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

05. 調(diào)試宏的第二種定義方式

調(diào)試宏的第二種定義方式

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

程序示例

#include < stdio.h >

#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;
}

「總結(jié):」

fmt必須是一個(gè)字符串,不能使用指針,只有這樣才可以實(shí)現(xiàn)字符串的功能。

06. 對(duì)調(diào)試語(yǔ)句進(jìn)行分級(jí)審查

即使定義了調(diào)試的宏,在工程足夠大的情況下,也會(huì)導(dǎo)致在打開(kāi)宏開(kāi)關(guān)的時(shí)候在終端出現(xiàn)大量的信息。而無(wú)法區(qū)分哪些是有用的。

這個(gè)時(shí)候就要加入分級(jí)檢查機(jī)制,可以定義不同的調(diào)試級(jí)別,這樣就可以對(duì)不同重要程序和不同的模塊進(jìn)行區(qū)分,需要調(diào)試哪一個(gè)模塊就可以打開(kāi)那一個(gè)模塊的調(diào)試級(jí)別。

一般可以利用配置文件的方式顯示,其實(shí)Linux內(nèi)核也是這么做的,它把調(diào)試的等級(jí)分成了7個(gè)不同重要程度的級(jí)別,只有設(shè)定某個(gè)級(jí)別可以顯示,對(duì)應(yīng)的調(diào)試信息才會(huì)打印到終端上。

可以寫(xiě)出一下配置文件

[debug]
debug_level=XXX_MODULE

解析配置文件使用標(biāo)準(zhǔn)的字符串操作庫(kù)函數(shù)就可以獲取XXX_MODULE這個(gè)數(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)試語(yǔ)句

在實(shí)際的開(kāi)發(fā)中,一般會(huì)維護(hù)兩種源程序,一種是帶有調(diào)試語(yǔ)句的調(diào)試版本程序,另外一種是不帶有調(diào)試語(yǔ)句的發(fā)布版本程序。

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

在實(shí)現(xiàn)過(guò)程中,可以使用一個(gè)調(diào)試宏來(lái)控制調(diào)試語(yǔ)句的開(kāi)關(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

定義條件編譯的方式使用一個(gè)帶有值的宏

#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

可以使用如下方式進(jìn)行條件編譯

#ifndef USE_DEBUG
#define USE_DEBUG 0
#endif

08. 使用do…while的宏定義

使用宏定義可以將一些較為短小的功能封裝,方便使用。宏的形式和函數(shù)類(lèi)似,但是可以節(jié)省函數(shù)跳轉(zhuǎn)的開(kāi)銷(xiāo)。

如何將一個(gè)語(yǔ)句封裝成一個(gè)宏,在程序中常常使用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. 代碼剖析

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

「測(cè)試程序:」

#include < stdio.h >


#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;
}

編譯的時(shí)候加入-pg選項(xiàng):

deng@itcast:~/tmp$ gcc -pg  test.c -o test

執(zhí)行完成后,在當(dāng)前文件中生成了一個(gè)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

其中主要的信息有兩個(gè),一個(gè)是每個(gè)函數(shù)執(zhí)行的時(shí)間占程序總時(shí)間的百分比,另外一個(gè)就是函數(shù)被調(diào)用的次數(shù)。通過(guò)這些信息,可以優(yōu)化核心程序的實(shí)現(xiàn)方式來(lái)提高效率。

當(dāng)然這個(gè)剖析程序由于它自身特性有一些限制,比較適用于運(yùn)行時(shí)間比較長(zhǎng)的程序,因?yàn)榻y(tǒng)計(jì)的時(shí)間是基于間隔計(jì)數(shù)這種機(jī)制,所以還需要考慮函數(shù)執(zhí)行的相對(duì)時(shí)間,如果程序執(zhí)行時(shí)間過(guò)短,那得到的信息是沒(méi)有任何參考意義的。

「將上訴程序時(shí)間縮短:」

#include < stdio.h >


#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;
}

「剖析結(jié)果如下:」

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

因此該剖析程序?qū)τ谠綇?fù)雜、執(zhí)行時(shí)間越長(zhǎng)的函數(shù)也適用。

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

#include < stdio.h >


#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;
}

「剖析結(jié)果如下:」

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

「總結(jié):」

在使用gprof工具的時(shí)候,對(duì)于一個(gè)函數(shù)進(jìn)行g(shù)prof方式的剖析,實(shí)質(zhì)上的時(shí)間是指除去庫(kù)函數(shù)調(diào)用和系統(tǒng)調(diào)用之外,純碎應(yīng)用部分開(kāi)發(fā)的實(shí)際代碼運(yùn)行的時(shí)間,也就是說(shuō)time一項(xiàng)描述的時(shí)間值不包括庫(kù)函數(shù)printf、系統(tǒng)調(diào)用system等運(yùn)行的時(shí)間。

這些實(shí)用庫(kù)函數(shù)的程序雖然運(yùn)行的時(shí)候?qū)⒈茸畛醯某绦驅(qū)嵱酶嗟臅r(shí)間,但是對(duì)于剖析函數(shù)來(lái)說(shuō)并沒(méi)有影響。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11511

    瀏覽量

    213837
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4381

    瀏覽量

    64898
  • 編譯程序
    +關(guān)注

    關(guān)注

    0

    文章

    13

    瀏覽量

    4203
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    LabVIEW編譯程序設(shè)計(jì)

      編譯程序設(shè)計(jì)是一個(gè)復(fù)雜的話題,即使對(duì)內(nèi)行的軟件工程師來(lái)說(shuō)也要考慮很多專(zhuān)業(yè)知識(shí)。   NI LabVIEW軟件是一種多規(guī)
    發(fā)表于 10-16 11:03 ?2824次閱讀
    LabVIEW<b class='flag-5'>編譯程序</b>設(shè)計(jì)

    Linux編程】如何使用gcc編譯源代碼時(shí)輸出map文件?

    Linux編程】如何使用gcc編譯源代碼時(shí)輸出map文件?
    的頭像 發(fā)表于 08-15 14:08 ?8946次閱讀
    【<b class='flag-5'>Linux</b>編程】如何使用<b class='flag-5'>gcc</b><b class='flag-5'>編譯</b>源代碼時(shí)輸出map文件?

    嵌入式軟件項(xiàng)目中的一些常用套路與技巧

    Linux使用gcc編譯程序的時(shí)候,對(duì)于調(diào)試的語(yǔ)句還具有一些特殊的語(yǔ)法
    發(fā)表于 05-23 11:04 ?483次閱讀

    LinuxGCC編譯

    一、Linux 下多文件編譯 在上一篇 Linux 下的 C 編程我們知道了 Linux 下的編譯器為
    的頭像 發(fā)表于 09-11 15:18 ?3089次閱讀
    <b class='flag-5'>Linux</b> 下<b class='flag-5'>GCC</b>的<b class='flag-5'>編譯</b>

    使用GCC編譯程序編譯過(guò)程

    一.使用GCC編譯程序時(shí),編譯過(guò)程可以細(xì)分為四個(gè)階段:預(yù)處理(Pre-Processing)編譯(Compiling)匯編(Assembling)鏈接(Linking)二.
    發(fā)表于 12-15 08:30

    為什么arm-linux-gcc無(wú)法編譯程序

    為什么arm-linux-gcc無(wú)法編譯程序呢?是什么原因造成的?求大神指導(dǎo)
    發(fā)表于 07-13 11:55

    鴻蒙系統(tǒng)移植:編譯第一個(gè)APP:hello

    不一樣): arm-linux-gcc??-o??hello? ?hello.c 在Liteos-a中,使用LLVM來(lái)編譯程序。LLVM的本意是“Low Level Virtual Machine”,一個(gè)底層的虛擬機(jī)。
    發(fā)表于 10-20 16:18 ?19次下載
    鴻蒙系統(tǒng)移植:<b class='flag-5'>編譯</b>第一個(gè)APP:hello

    關(guān)于嵌入式Linux調(diào)試相關(guān)的宏

    Linux使用gcc編譯程序的時(shí)候,對(duì)于調(diào)試的語(yǔ)句還具有一些特殊的語(yǔ)法。gcc編譯的過(guò)程中,會(huì)
    的頭像 發(fā)表于 07-06 16:29 ?1163次閱讀

    Linux編程】如何使用gcc生成預(yù)編譯文件?

    Linux編程】如何使用gcc生成預(yù)編譯文件?
    的頭像 發(fā)表于 08-31 13:07 ?2263次閱讀
    【<b class='flag-5'>Linux</b>編程】如何使用<b class='flag-5'>gcc</b>生成預(yù)<b class='flag-5'>編譯</b>文件?

    RX族C/C++編譯程序、匯編程序、優(yōu)化連接編輯程序 編譯程序包 用戶手冊(cè) Rev.1.00

    RX族C/C++編譯程序、匯編程序、優(yōu)化連接編輯程序 編譯程序包 用戶手冊(cè) Rev.1.00
    發(fā)表于 04-17 19:24 ?0次下載
    RX族C/C++<b class='flag-5'>編譯程序</b>、匯編<b class='flag-5'>程序</b>、優(yōu)化連接編輯<b class='flag-5'>程序</b> <b class='flag-5'>編譯程序</b>包 用戶手冊(cè) Rev.1.00

    SuperHTM RISC引擎C/C++編譯程序、匯總程序、優(yōu)化連接編譯程序 編譯程序包V.9.01 用戶手冊(cè)

    SuperHTM RISC引擎C/C++編譯程序、匯總程序、優(yōu)化連接編譯程序 編譯程序包V.9.01 用戶手冊(cè)
    發(fā)表于 05-04 18:40 ?0次下載
    SuperHTM RISC引擎C/C++<b class='flag-5'>編譯程序</b>、匯總<b class='flag-5'>程序</b>、優(yōu)化連接<b class='flag-5'>編譯程序</b> <b class='flag-5'>編譯程序</b>包V.9.01 用戶手冊(cè)

    H8S、H8/300系列C/C++編譯程序、匯總程序、優(yōu)化連接編譯程序(RCJ10B0001-0100)

    H8S、H8/300系列C/C++編譯程序、匯總程序、優(yōu)化連接編譯程序(RCJ10B0001-0100)
    發(fā)表于 05-09 20:00 ?0次下載
    H8S、H8/300系列C/C++<b class='flag-5'>編譯程序</b>、匯總<b class='flag-5'>程序</b>、優(yōu)化連接<b class='flag-5'>編譯程序</b>(RCJ10B0001-0100)

    Linux C開(kāi)發(fā)中的一些常用的調(diào)試技巧

    Linux使用gcc編譯程序的時(shí)候,對(duì)于調(diào)試的語(yǔ)句還具有一些特殊的語(yǔ)法。
    發(fā)表于 06-01 09:03 ?354次閱讀

    H8S、H8/300系列C/C++編譯程序、匯總程序、優(yōu)化連接編譯程序(RCJ10B0001-0100)

    H8S、H8/300系列C/C++編譯程序、匯總程序、優(yōu)化連接編譯程序(RCJ10B0001-0100)
    發(fā)表于 06-28 18:50 ?0次下載
    H8S、H8/300系列C/C++<b class='flag-5'>編譯程序</b>、匯總<b class='flag-5'>程序</b>、優(yōu)化連接<b class='flag-5'>編譯程序</b>(RCJ10B0001-0100)

    嵌入式Linux C語(yǔ)言編程程序調(diào)試與宏定義

    Linux使用gcc編譯程序的時(shí)候,對(duì)于調(diào)試的語(yǔ)句還具有一些特殊的語(yǔ)法gcc編譯的過(guò)程中,會(huì)
    發(fā)表于 03-01 11:41 ?1469次閱讀