久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

專(zhuān)注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

C/C++中宏定義的經(jīng)典運(yùn)用

作者:huqin   來(lái)源:本站原創(chuàng)   點(diǎn)擊數(shù):  更新時(shí)間:2014年03月13日   【字體:

   新年快樂(lè),好好的玩了幾天,張家港二日游讓我有了較大的感觸,蘇南發(fā)展的真心好哦,想想老家的發(fā)展真的覺(jué)得是落后哦,回學(xué)校看了一些大牛的博客,貌似在假期中大牛們才有時(shí)間寫(xiě)博客。祝大家新年快樂(lè),身體健康。

   言歸正傳,在C語(yǔ)言中宏定義是比較有用的技巧,在Linux源碼中經(jīng)常使用一些宏定義,比如宏container_of()等都是經(jīng)典的宏定義表示方式。在C++不再主張使用宏定義,但是宏定義實(shí)際上卻是是一個(gè)非常有用的手段。實(shí)質(zhì)上宏定義能夠搞定的實(shí)現(xiàn)采用其它的實(shí)現(xiàn)也是可以的,宏定義的作用是簡(jiǎn)單的替代作用,掌握這個(gè)是理解的關(guān)鍵,以前在沒(méi)有代碼閱讀量的時(shí)候總是以為宏定義就是簡(jiǎn)單的定義一些常量什么的,實(shí)質(zhì)上不然,宏定義完全可以寫(xiě)成函數(shù)的形式,但是宏定義和函數(shù)有一定的差別,函數(shù)的調(diào)用一般采用棧的方式實(shí)現(xiàn),這時(shí)候存在局部變量,形參、實(shí)參等問(wèn)題,如果不理解C語(yǔ)言的本質(zhì),很多時(shí)候非常容易搞錯(cuò),但是宏定義則不然,宏定義沒(méi)有調(diào)用的過(guò)程,宏的實(shí)現(xiàn)僅僅是一個(gè)替換過(guò)程,不用壓棧出棧,宏實(shí)現(xiàn)的類(lèi)似函數(shù)修改的就是當(dāng)前區(qū)域中的變量,不是局部變量。這也是一些較深層次的問(wèn)題,在剛學(xué)習(xí)C語(yǔ)言的時(shí)候很多人只要看見(jiàn)類(lèi)似于函數(shù)的形式都認(rèn)為是函數(shù),實(shí)質(zhì)上不一定,很有可能就是采用宏定義實(shí)現(xiàn)的類(lèi)函數(shù),這時(shí)候就可能導(dǎo)致所謂的形參實(shí)參問(wèn)題發(fā)生較大的變化。關(guān)于宏的問(wèn)題在面試筆試的過(guò)程中、寫(xiě)代碼的過(guò)程中都是非常有用的部分,我就不再做介紹。

   今天看了一遍博客《Reduce C-language coding errors with X macros》,感覺(jué)文章寫(xiě)得非常的好,也對(duì)自己寫(xiě)代碼有了一定的幫助,所以就將該文章用我自己的語(yǔ)言,寫(xiě)出來(lái)和大家分享分享吧。

    在嵌入式實(shí)時(shí)操作系統(tǒng)中,經(jīng)常將系統(tǒng)分成很多層次,很多個(gè)模塊,每一個(gè)模塊都有自己的初始化過(guò)程,這時(shí)候我們一般采用的形式如下所示:

    typedef void(*p_func_t)(void);

    enum
    {
        STATE_0,
        STATE_1,
        STATE_2,
        ...
        STATE_M,
        NUM_STATES
    };

    p_func_t inital_table[NUM_STATES]
    {
       func_0,
       func_1,
       func_2,
       ...
       func_M,
    };

   這種實(shí)現(xiàn)方法是比較常見(jiàn)的實(shí)現(xiàn)方式,但是這種方法的缺點(diǎn)是所有的初始化過(guò)程是按照一定的順序的,而且不能隨機(jī)的初始化,因此如果在編碼的過(guò)程中將狀態(tài)號(hào)與初始化函數(shù)對(duì)應(yīng)錯(cuò)誤,將出現(xiàn)比較難以發(fā)現(xiàn)的錯(cuò)誤,這種錯(cuò)誤經(jīng)常出現(xiàn),當(dāng)然有些編譯器以及支持隨機(jī)的初始化過(guò)程,但是并不具有通用性,而且這種實(shí)現(xiàn)方式代碼比較多,能不能采用宏定義的方式簡(jiǎn)化代碼量呢?當(dāng)然是可以的,采用類(lèi)似于函數(shù)的宏定義就可以實(shí)現(xiàn),具體的實(shí)現(xiàn)如下:

    typedef void(*p_func_t)(void);

    #define STATE_TABLE\
        ENTRY(STATE_0,func_0)\
        ENTRY(STATE_1,func_1)\
        ENTRY(STATE_2,func_2)\
        ENTRY(STATE_3,func_3)\
        ENTRY(STATE_4,func_4)

    enum{
    #define ENTRY(a,b)    a,
        STATE_TABLE
    #undef ENTRY
      NUM_STATES
    };

    p_func_t inital_table[NUM_STATES] =
    {
    #define ENTRY(a,b)    b,
        STATE_TABLE
    #undef ENTRY
     };

   上面這種實(shí)現(xiàn)方式的優(yōu)點(diǎn)是運(yùn)用了宏定義簡(jiǎn)少代碼量。我做一個(gè)簡(jiǎn)要的分析,首先采用宏定義定義了一組ENYRT,其中包含兩個(gè)參數(shù),分別是狀態(tài)號(hào)STATE_N,和狀態(tài)對(duì)應(yīng)的初始化函數(shù),這種實(shí)現(xiàn)方式能夠避免上面所謂的狀態(tài)號(hào)與函數(shù)對(duì)應(yīng)錯(cuò)誤的問(wèn)題,因?yàn)樵诤甓x的過(guò)程中一般都會(huì)認(rèn)真的確定各種接口,對(duì)應(yīng)好了只需要定義相關(guān)的函數(shù)就可以啦。在enum中采用了#define和#undef來(lái)限定這一組宏定義的作用范圍,在個(gè)作用域中,ENTRY(a,b)是表示“a,”,需要注意不能忽略a后的','因?yàn)檫@就是在enum中定義變量后要添加的符號(hào),我想大家應(yīng)該知道enum{a,b,c,d}每一個(gè)成員后面都包含","的特性的。也就是說(shuō)在這作用域中,ENTRY(a,b)被替換為"a,",那么這時(shí)候STATE_TABLE就被替換為STATE_0,STATE_1等,然后和NUM_STATES就組成了第一個(gè)例程中的enum結(jié)構(gòu)。而在p_func_t jumptable[NUM_STATES]仍然采用了了STATE_TABLE,由于采用了#define和#undef限定了宏的作用范圍,這時(shí)的ENTRY(a,b)將被替代為“b,”,也就是func_0,func_1等,這樣也就完成了函數(shù)指針數(shù)組的初始化過(guò)程,這樣的初始化能夠減少狀態(tài)號(hào)與初始化函數(shù)對(duì)應(yīng)出錯(cuò)的情況。

   這樣的實(shí)現(xiàn)也可以認(rèn)為是宏定義的巧妙運(yùn)用,但是這種方法還是存在一些問(wèn)題,因?yàn)椴捎?define 和#undef這種方法很可能導(dǎo)致錯(cuò)誤的產(chǎn)生,因?yàn)楹苡锌赡懿荒芎芎玫陌盐者@個(gè)限定作用域的使用方法,這時(shí)候可以采用一種新的類(lèi)似函數(shù)的實(shí)現(xiàn)方法,可以讓STATE_TABLE帶一個(gè)參數(shù),也就是采用類(lèi)似命令的形式定義相關(guān)的內(nèi)容:

    typedef void(*p_func_t)(void);

    /*以下產(chǎn)生幾個(gè)常用的命令*/
    /*enum產(chǎn)生*/
    #define EXPAND_AS_ENUM(a,b) a,
    /*初始化表產(chǎn)生*/
    #define EXPAND_AS_INITTABLE(a,b) b,
    /*聲明各個(gè)函數(shù)*/
    #define EXPAND_AS_FUNCDEC(a,b) void b(void);

    /*將STATE_TABLE的參數(shù)就是具體的命令*/
    #define STATE_TABLE(ENTRY)\
        ENTRY(STATE_0,func_0)\
        ENTRY(STATE_1,func_1)\
        ENTRY(STATE_2,func_2)\
        ENTRY(STATE_3,func_3)\
        ENTRY(STATE_4,func_4)

    /*定義enum*/
    enum{
      STATE_TABLE(EXPAND_AS_ENUM)
      NUM_STATES
    };

    /*聲明各個(gè)函數(shù)*/
    STATE_TABLE(EXPAND_AS_FUNCDEC)

    /*初始化表*/
    p_func_t inital_table[NUM_STATES] =
    {
        STATE_TABLE(EXPAND_AS_INITTABLE)
     };

   以上實(shí)現(xiàn)方法能夠較好的避免#define和#undef的限定作用域問(wèn)題,這實(shí)際上采用ENTRY作為參數(shù)傳遞給STATE_TABLE,然后ENTRY可用來(lái)實(shí)現(xiàn)不同的指令,這些指令的定義也是一系列的宏定義,這種實(shí)現(xiàn)架構(gòu)能夠比較好的避免缺少聲明等問(wèn)題。同時(shí)也較少了錯(cuò)誤的產(chǎn)生可能。

這種實(shí)現(xiàn)模型只是簡(jiǎn)化的版本,STATE_TABLE(ENRTY)中的ENTRY可以定義多個(gè)參數(shù)如下所示:

    /*COMMANDS*/
    #define COMD1(a0,a1,a2,...,am) /*具體實(shí)現(xiàn)*/
    #define COMD2(a0,a1,a2,...,am) /*具體實(shí)現(xiàn)*/
    ...

    #define TABLE(ENTRY)\
       ENTRY(a0,a1,a2,...,am)\
       ENTRY(a0,a1,a2,...,am)\
       ...
       ENTRY(a0,a1,a2,...,am)\

   比如將上面的初始化實(shí)現(xiàn)修改為下面的形式,也就是多個(gè)參數(shù)的形式,實(shí)現(xiàn)如下:

點(diǎn)擊(此處)折疊或打開(kāi)

    typedef void(*p_func_t)(void);

    #define EXPAND_AS_ENUM(a,b,c) a,

    #define EXPAND_AS_JUMPTABLE(a,b,c) b,

    #define EXPAND_AS_FUNCDEC(a,b,c) void c(void);

    #define STATE_TABLE(ENTRY)\
        ENTRY(STATE_0,func_0,func_0)\
        ENTRY(STATE_1,func_1,func_1)\
        ENTRY(STATE_2,func_2,func_2)\
        ENTRY(STATE_3,func_3,func_3)\
        ENTRY(STATE_4,func_4,func_4)


    enum{
      STATE_TABLE(EXPAND_AS_ENUM)
      NUM_STATES
    };

    STATE_TABLE(EXPAND_AS_FUNCDEC)

    p_func_t init_table[NUM_STATES] =
    {
       STATE_TABLE(EXPAND_AS_INITTABLE)
    };

   上面的實(shí)現(xiàn)并不是非常的恰當(dāng),因?yàn)榈诙䝼(gè)、第三個(gè)參數(shù)實(shí)質(zhì)上是一致的,沒(méi)有必要定義為三個(gè)參數(shù)。本文只是說(shuō)明三個(gè)參數(shù)的實(shí)現(xiàn)情況。其他多個(gè)參數(shù)的實(shí)現(xiàn)情況類(lèi)似。為了說(shuō)明這種模型的可行性,我寫(xiě)了簡(jiǎn)單的測(cè)試代碼,由于各個(gè)模塊的初始化代碼需要程序員手動(dòng)的實(shí)現(xiàn),因此可以定義在其他的位置,同時(shí)在宏定義中也已經(jīng)實(shí)現(xiàn)了各個(gè)函數(shù)的聲明問(wèn)題,因?yàn)椴粫?huì)出現(xiàn)未定義的問(wèn)題。具體的實(shí)現(xiàn)如下所示:

    #include<stdio.h>

    typedef void(*p_func_t)(void);

    #define EXPAND_AS_ENUM(a,b) a,

    #define EXPAND_AS_INITTABLE(a,b) b,

    #define EXPAND_AS_FUNCDEC(a,b) void b(void);

    #define STATE_TABLE(ENTRY)\
        ENTRY(STATE_0,func_0)\
        ENTRY(STATE_1,func_1)\
        ENTRY(STATE_2,func_2)\
        ENTRY(STATE_3,func_3)\
        ENTRY(STATE_4,func_4)


    enum{
      STATE_TABLE(EXPAND_AS_ENUM)
      NUM_STATES
    };

    STATE_TABLE(EXPAND_AS_FUNCDEC)

    p_func_t init_table[NUM_STATES] =
    {
        STATE_TABLE(EXPAND_AS_JUMPTABLE)
     };

 

    /*測(cè)試代碼*/
    int main()
    {
        int i = 0;

        for(i = 0; i < NUM_STATES; ++ i)
            (jumptable[i])();

        return 0;
    }

    /*各個(gè)模塊的初始化函數(shù)實(shí)現(xiàn)*/
    void func_0(void)
    {
        printf("In func_0\n");
    }

    void func_1(void)
    {
        printf("In func_1\n");
    }

    void func_2(void)
    {
        printf("In func_2\n");
    }

    void func_3(void)
    {
        printf("In func_3\n");
    }

    void func_4(void)
    {
        printf("In func_4\n");
    }

   關(guān)于多變量的情況,在Linux內(nèi)核源碼中的物理內(nèi)存與虛擬內(nèi)存之間可以采用這種方式實(shí)現(xiàn),在很多情況下都知道寄存器的物理內(nèi)存,一般一組相關(guān)的寄存器的映射方式都是相同的,采用這種宏定義的實(shí)現(xiàn)方式就能較好完成定義問(wèn)題,當(dāng)然只是可行的方法而已。

    #define ADDRESS_OFFSET (0x8000)
    #define PHYS_ADDRESS(a,b,c) a=c;
    #define VIRU_ADDRESS(a,b,c) a = b + c;

    #define REGISTER_MAP(ENTRY)
       ENTRY(reg0,ADDRESS_OFFSET,0x10)\
       ENTRY(reg1,ADDRESS_OFFSET,0x14)\
       ENTRY(reg2,ADDRESS_OFFSET,0x18)\
       ...
       ENTRY(regm,ADDRESS_OFFSET,0x2c)

    /*物理地址*/
    REGISTER_MAP(PHYS_ADDRESS)
    /*虛擬地址*/
    REGISTER_MAP(VIRU_ADDRESS)

   宏定義的這種實(shí)現(xiàn)方式在一些嵌入式系統(tǒng)中是非常有效的,掌握這種實(shí)現(xiàn)方法能夠較好的管理各個(gè)模塊、各種狀態(tài)下對(duì)應(yīng)的處理函數(shù)。這是一種經(jīng)典的用法。這實(shí)際上給出可一種解決問(wèn)題的模型架構(gòu),掌握好這種方式能夠較好的實(shí)現(xiàn)模塊的管理問(wèn)題。這種實(shí)現(xiàn)方法不僅代碼量少,而且能夠避免很多錯(cuò)誤的產(chǎn)生,能夠快速的進(jìn)行修改,但是難點(diǎn)在于代碼對(duì)于初學(xué)者有一定的難度,而且宏定義實(shí)現(xiàn)的函數(shù)還能夠采用其他的方法實(shí)現(xiàn),只是宏定義能夠較好的簡(jiǎn)化代碼,使得代碼優(yōu)美且易維護(hù)。

關(guān)閉窗口

相關(guān)文章

主站蜘蛛池模板: 四色永久 | 99视频免费在线观看 | 亚洲成人免费在线观看 | 91精品国产综合久久久密闭 | 欧美激情在线精品一区二区三区 | 中文字幕国产精品视频 | 青青久在线视频 | 瑟瑟免费视频 | 天天人人精品 | 一区二区三区四区视频 | 成人一区二区三区在线观看 | 久久av资源网 | 久久aⅴ乱码一区二区三区 91综合网 | 草久在线| 日韩在线大片 | 亚洲一区二区在线电影 | 美女天天操 | 国产在线观看不卡一区二区三区 | 91中文在线观看 | 嫩草一区二区三区 | 婷婷在线视频 | 精精国产xxxx视频在线 | 综合久久99 | 亚洲一区二区三区久久 | 欧美中文一区 | 国产黄色在线观看 | 亚洲精品视频久久 | 视频一区二区三区中文字幕 | 国产婷婷在线视频 | 视频一区二区在线观看 | 涩涩片影院| 欧美成人一级 | 中文字幕在线视频一区二区三区 | 一区二区三区国产 | 欧美一区二区免费在线 | 欧美视频一区二区三区 | 欧美成人h版在线观看 | 一区二区三区视频在线观看 | 亚洲精品一区在线 | 亚洲免费影院 | 91欧美激情一区二区三区成人 |