最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C/C++ 宏詳細(xì)解析

 更新時(shí)間:2013年09月17日 09:32:43   投稿:jingxian  
關(guān)于宏的一些語法問題,可以在google上找到。相信我,你對于宏的了解絕對沒你想象的那么多。如果你還不知道#和##,也不知道prescan,那么你肯定對宏的了解不夠

眾多C++書籍都忠告我們C語言宏是萬惡之首,但事情總不如我們想象的那么壞,就如同goto一樣。宏有一個(gè)很大的作用,就是自動為我們產(chǎn)生代碼。如果說模板可以為我們產(chǎn)生各種型別的代碼(型別替換),那么宏其實(shí)可以為我們在符號上產(chǎn)生新的代碼(即符號替換、增加)。

關(guān)于宏的一些語法問題,可以在google上找到。相信我,你對于宏的了解絕對沒你想象的那么多。如果你還不知道#和##,也不知道prescan,那么你肯定對宏的了解不夠。

我稍微講解下宏的一些語法問題(說語法問題似乎不妥,macro只與preprocessor有關(guān),跟語義分析又無關(guān)):

1. 宏可以像函數(shù)一樣被定義,例如:
#define min(x,y) (x 但是在實(shí)際使用時(shí),只有當(dāng)寫上min(),必須加括號,min才會被作為宏展開,否則不做任何處理。

2. 如果宏需要參數(shù),你可以不傳,編譯器會給你警告(宏參數(shù)不夠),但是這會導(dǎo)致錯(cuò)誤。如C++書籍中所描述的,編譯器(預(yù)處理器)對宏的語法檢查不夠,所以更多的檢查性工作得你自己來做。

3. 很多程序員不知道的#和##
#符號把一個(gè)符號直接轉(zhuǎn)換為字符串,例如:
#define STRING(x) #x
const char *str = STRING( test_string ); str的內(nèi)容就是"test_string",也就是說#會把其后的符號直接加上雙引號。
##符號會連接兩個(gè)符號,從而產(chǎn)生新的符號(詞法層次),例如:
#define SIGN( x ) INT_##x
int SIGN( 1 ); 宏被展開后將成為:int INT_1;

4. 變參宏,這個(gè)比較酷,它使得你可以定義類似的宏:
#define LOG( format, ... ) printf( format, __VA_ARGS__ )
LOG( "%s %d", str, count );
__VA_ARGS__是系統(tǒng)預(yù)定義宏,被自動替換為參數(shù)列表。

5. 當(dāng)一個(gè)宏自己調(diào)用自己時(shí),會發(fā)生什么?例如:
#define TEST( x ) ( x + TEST( x ) )
TEST( 1 ); 會發(fā)生什么?為了防止無限制遞歸展開,語法規(guī)定,當(dāng)一個(gè)宏遇到自己時(shí),就停止展開,也就是說,當(dāng)對TEST( 1 )進(jìn)行展開時(shí),展開過程中又發(fā)現(xiàn)了一個(gè)TEST,那么就將這個(gè)TEST當(dāng)作一般的符號。TEST(1)
最終被展開為:1 + TEST( 1) 。

6. 宏參數(shù)的prescan,當(dāng)一個(gè)宏參數(shù)被放進(jìn)宏體時(shí),這個(gè)宏參數(shù)會首先被全部展開(有例外,見下文)。當(dāng)展開后的宏參數(shù)被放進(jìn)宏體時(shí),預(yù)處理器對新展開的宏體進(jìn)行第二次掃描,并繼續(xù)展開。例如:
#define PARAM( x ) x
#define ADDPARAM( x ) INT_##x
PARAM( ADDPARAM( 1 ) );
因?yàn)锳DDPARAM( 1 ) 是作為PARAM的宏參數(shù),所以先將ADDPARAM( 1 )展開為INT_1,然后再將INT_1放進(jìn)PARAM。

例外情況是,如果PARAM宏里對宏參數(shù)使用了#或##,那么宏參數(shù)不會被展開:
#define PARAM( x ) #x
#define ADDPARAM( x ) INT_##x
PARAM( ADDPARAM( 1 ) ); 將被展開為"ADDPARAM( 1 )"。

使用這么一個(gè)規(guī)則,可以創(chuàng)建一個(gè)很有趣的技術(shù):打印出一個(gè)宏被展開后的樣子,這樣可以方便你分析代碼:
#define TO_STRING( x ) TO_STRING1( x )
#define TO_STRING1( x ) #x
TO_STRING首先會將x全部展開(如果x也是一個(gè)宏的話),然后再傳給TO_STRING1轉(zhuǎn)換為字符串,現(xiàn)在你可以這樣:
const char *str = TO_STRING( PARAM( ADDPARAM( 1 ) ) );去一探PARAM展開后的樣子。

7. 一個(gè)很重要的補(bǔ)充:就像我在第一點(diǎn)說的那樣,如果一個(gè)像函數(shù)的宏在使用時(shí)沒有出現(xiàn)括號,那么預(yù)處理器只是將這個(gè)宏作為一般的符號處理(那就是不處理)。

我們來見識一下宏是如何幫助我們自動產(chǎn)生代碼的。如我所說,宏是在符號層次產(chǎn)生代碼。我在分析Boost.Function模塊時(shí),因?yàn)樗褂昧舜罅康暮?宏嵌套,再嵌套),導(dǎo)致我壓根沒看明白代碼。后來發(fā)現(xiàn)了一個(gè)小型的模板庫ttl,說的是開發(fā)一些小型組件去取代部分Boost(這是一個(gè)好理由,因?yàn)锽oost確實(shí)太大)。同樣,這個(gè)庫也包含了一個(gè)function庫。

這里的function也就是我之前提到的functor。ttl.function庫里為了自動產(chǎn)生很多類似的代碼,使用了一個(gè)宏:

#define TTL_FUNC_BUILD_FUNCTOR_CALLER(n) /
template< typename R, TTL_TPARAMS(n) > /
struct functor_caller_base##n /
///...
該宏的最終目的是:通過類似于TTL_FUNC_BUILD_FUNCTOR_CALLER(1)的調(diào)用方式,自動產(chǎn)生很多functor_caller_base模板:
template struct functor_caller_base1
template struct functor_caller_base2
template struct functor_caller_base3
///...
那么,核心部分在于TTL_TPARAMS(n)這個(gè)宏,可以看出這個(gè)宏最終產(chǎn)生的是:
typename T1
typename T1, typename T2
typename T1, typename T2, typename T3
///...
我們不妨分析TTL_TPARAMS(n)的整個(gè)過程。分析宏主要把握我以上提到的一些要點(diǎn)即可。以下過程我建議你翻著ttl的代碼,
相關(guān)代碼文件:function.hpp, macro_params.hpp, macro_repeat.hpp, macro_misc.hpp, macro_counter.hpp。

so, here we go

分析過程,逐層分析,逐層展開,例如TTL_TPARAMS(1):
#define TTL_TPARAMS(n) TTL_TPARAMSX(n,T)
=> TTL_TPARAMSX( 1, T )
#define TTL_TPARAMSX(n,t) TTL_REPEAT(n, TTL_TPARAM, TTL_TPARAM_END, t)
=> TTL_REPEAT( 1, TTL_TPARAM, TTL_TPARAM_END, T )
#define TTL_TPARAM(n,t) typename t##n,
#define TTL_TPARAM_END(n,t) typename t##n
#define TTL_REPEAT(n, m, l, p) TTL_APPEND(TTL_REPEAT_, TTL_DEC(n))(m,l,p) TTL_APPEND(TTL_LAST_REPEAT_,n)(l,p)

注意,TTL_TPARAM, TTL_TPARAM_END雖然也是兩個(gè)宏,他們被作為TTL_REPEAT宏的參數(shù),按照prescan規(guī)則,似乎應(yīng)該先將這兩個(gè)宏展開再傳給TTL_REPEAT。但是,如同我在前面重點(diǎn)提到的,這兩個(gè)宏是function-like macro,使用時(shí)需要加括號,如果沒加括號,則不當(dāng)作宏處理。因此,展開TTL_REPEAT時(shí),應(yīng)該為:
=> TTL_APPEND( TTL_REPEAT_, TTL_DEC(1))(TTL_TPARAM,TTL_TPARAM_END,T) TTL_APPEND( TTL_LAST_REPEAT_,1)(
TTL_TPARAM_END,T)

這個(gè)宏體看起來很復(fù)雜,仔細(xì)分析下,可以分為兩部分:
TTL_APPEND( TTL_REPEAT_, TTL_DEC(1))(TTL_TPARAM,TTL_TPARAM_END,T)以及
TTL_APPEND( TTL_LAST_REPEAT_,1)(TTL_TPARAM_END,T)

先分析第一部分:
#define TTL_APPEND( x, y ) TTL_APPEND1(x,y) //先展開x,y再將x,y連接起來
#define TTL_APPEND1( x, y ) x ## y
#define TTL_DEC(n) TTL_APPEND(TTL_CNTDEC_, n)

根據(jù)先展開參數(shù)的原則,會先展開TTL_DEC(1)
=> TTL_APPEND(TTL_CNTDEC_,1) => TTL_CNTDEC_1
#define TTL_CNTDEC_1 0 注意,TTL_CNTDEC_不是宏,TTL_CNTDEC_1是一個(gè)宏。
=> 0 , 也就是說,TTL_DEC(1)最終被展開為0。回到TTL_APPEND部分:
=> TTL_REPEAT_0 (TTL_TPARAM,TTL_TPARAM_END,T)
#define TTL_REPEAT_0(m,l,p)

TTL_REPEAT_0這個(gè)宏為空,那么,上面說的第一部分被忽略,現(xiàn)在只剩下第二部分:
TTL_APPEND( TTL_LAST_REPEAT_,1)(TTL_TPARAM_END,T)
=> TTL_LAST_REPEAT_1 (TTL_TPARAM_END,T) // TTL_APPEND將TTL_LAST_REPEAT_和1合并起來
#define TTL_LAST_REPEAT_1(m,p) m(1,p)
=> TTL_TPARAM_END( 1, T )
#define TTL_TPARAM_END(n,t) typename t##n
=> typename T1 展開完畢。

相關(guān)文章

  • 如何判斷一個(gè)數(shù)是否為2的冪次方?若是,并判斷出來是多少次方?

    如何判斷一個(gè)數(shù)是否為2的冪次方?若是,并判斷出來是多少次方?

    本篇文章是對如何判斷一個(gè)數(shù)是否為2的冪次方?若是,并判斷出來是多少次方的實(shí)現(xiàn)方法,進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C++ vector如何動態(tài)申請內(nèi)存的元素

    C++ vector如何動態(tài)申請內(nèi)存的元素

    vector是一種動態(tài)數(shù)組,本文主要介紹了C++ vector如何動態(tài)申請內(nèi)存的元素,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • C語言版約瑟夫問題算法實(shí)現(xiàn)

    C語言版約瑟夫問題算法實(shí)現(xiàn)

    大家好,本篇文章主要講的是C語言版約瑟夫問題算法實(shí)現(xiàn),感興趣的同學(xué)趕快來看一看吧,對你又幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • C語言通過棧實(shí)現(xiàn)小人走迷宮

    C語言通過棧實(shí)現(xiàn)小人走迷宮

    這篇文章主要為大家詳細(xì)介紹了C語言通過棧實(shí)現(xiàn)小人走迷宮,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • C++之std命名空間

    C++之std命名空間

    這篇文章主要介紹了C++之std命名空間使用,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • C++構(gòu)建函數(shù)使用介紹

    C++構(gòu)建函數(shù)使用介紹

    構(gòu)造函數(shù)主要作用在于創(chuàng)建對象時(shí)為對象的成員屬性賦值,構(gòu)造函數(shù)由編譯器自動調(diào)用,無須手動調(diào)用;析構(gòu)函數(shù)主要作用在于對象銷毀前系統(tǒng)自動調(diào)用,執(zhí)行一 些清理工作
    2022-08-08
  • 最新C/C++中的new和delete的實(shí)現(xiàn)過程小結(jié)

    最新C/C++中的new和delete的實(shí)現(xiàn)過程小結(jié)

    這篇文章主要介紹了C/C++中的new和delete的實(shí)現(xiàn)過程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • C、C++線性表基本操作的詳細(xì)介紹

    C、C++線性表基本操作的詳細(xì)介紹

    這篇文章主要給大家介紹了關(guān)于C、C++線性表基本操作的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • C++中的聚合類定義與用法分析

    C++中的聚合類定義與用法分析

    這篇文章主要介紹了C++中的聚合類定義與用法,結(jié)合實(shí)例形式分析了C++中聚合類的簡單定義、使用方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-08-08
  • C#使用反射加載多個(gè)程序集的實(shí)現(xiàn)方法

    C#使用反射加載多個(gè)程序集的實(shí)現(xiàn)方法

    下面小編就為大家?guī)硪黄狢#使用反射加載多個(gè)程序集的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-06-06

最新評論

福安市| 连江县| 闸北区| 磐安县| 西贡区| 静海县| 梁河县| 濮阳市| 阳山县| 湄潭县| 杨浦区| 花垣县| 鄢陵县| 潞西市| 榆中县| 驻马店市| 横峰县| 绥芬河市| 苏州市| 宜章县| 浮山县| 婺源县| 浠水县| 安顺市| 琼海市| 平江县| 雅江县| 淮南市| 诸城市| 会同县| 体育| 比如县| 京山县| 军事| 天等县| 资兴市| 威宁| 延长县| 大足县| 神池县| 沁阳市|