C宏——智者的利刃,愚者的惡夢!

來源:互聯網
上載者:User

原文地址:http://hi.baidu.com/joyeep/blog/item/2ad5f662e00f41dce7113a37.html

水平不高不低的C++程式員最喜歡掛在嘴上的一句話就是:C宏,萬惡之首,錯誤的開端,應該被棄。  

例一、用C宏,書寫代碼更簡潔這段代碼寫網路程式的朋友都很眼熟,是Net/3中mbuf的實現。

struct mbuf{      struct m_hdr mhdr;      union {          struct           {              struct pkthdr MH_pkthdr; /* M_PKTHDR set */              union               {                  struct m_ext MH_ext; /* M_EXT set */                  char MH_databuf[MHLEN];              } MH_dat;          } MH;          char M_databuf[MLEN];          /* !M_PKTHER, !M_EXT*/      } M_dat;};

  上面的代碼,假如我想訪問最裡層的MH_databuf,那麼我必須寫M_dat.MH.MH_dat.MH_databuf; 這是不是很長,很難寫呀?這樣的代碼閱讀起來也不明了。其實,對於MH_pkthdr、MH_ext、MH_databuf來說,雖然不是在一個結構層次上,但是如果我們站在mbuf之外來看,它們都是mbuf的屬性,完全可以壓扁到一個平面上去看。所以,源碼中有這麼一組宏:

#define m_next        m_hdr.mh_next#define m_len         m_hdr.mh_len#define m_data        m_hdr.mh_data... ...#define m_pkthdr      M_dat.MH.MH_pkthdr#define m_pktdat      M_dat.MH.MH_dat.MH_databuf... ...

這樣寫起代碼來,是不是很精練呢!

例二、用C宏,實現跨平台和編譯器的需要這方面的例子太好舉了,一舉一大摞,就從VC的庫源碼中隨意copy一段出來吧。

#ifndef _CRTAPI1#if _MSC_VER >= 800 && _M_IX86 >= 300#define _CRTAPI1 __cdecl#else    /* _MSC_VER >= 800 && _M_IX86 >= 300 */#define _CRTAPI1#endif    /* _MSC_VER >= 800 && _M_IX86 >= 300 */#endif    /* _CRTAPI1 */#ifndef _SIZE_T_DEFINEDtypedef unsigned int size_t;#define _SIZE_T_DEFINED#endif    /* _SIZE_T_DEFINED */#ifndef _MAC#ifndef _WCHAR_T_DEFINEDtypedef unsigned short wchar_t;#define _WCHAR_T_DEFINED#endif    /* _WCHAR_T_DEFINED */#endif    /* _MAC */ #ifndef _NLSCMP_DEFINED#define _NLSCMPERROR      2147483647    /* currently == INT_MAX */#define _NLSCMP_DEFINED#endif    /* _NLSCMP_DEFINED */

請問,這些指示宏如何取代呢?如果真的是沒有了這些宏,實現起來就更麻煩了吧。

例三、用C宏,自動產生代碼這方面的例子也是多得很,不過有鑒於很多朋友不用很多編譯器,不做嵌入式的開發,我就舉個win平台的例子吧。我們知道MFC實現了windows的訊息映射,比如:

ON_COMMAND(IDM_ABOUT, OnAbout)ON_COMMAND(IDM_FILENEW, OnFileNew)

它是如何?的IDM_ABOUT和OnAbout的關聯的呢?這要用到幾個宏。

#define DECLARE_MESSAGE_MAP() \private: \ static const AFX_MSGMAP_ENTRY _messageEntries[]; \protected: \ static AFX_DATA const AFX_MSGMAP messageMap; \ virtual const AFX_MSGMAP* GetMessageMap() const; \#define BEGIN_MESSAGE_MAP(theClass, baseClass) \ const AFX_MSGMAP* theClass::GetMessageMap() const \    { return &theClass::messageMap; } \ AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \ { &baseClass::messageMap, &theClass::_messageEntries[0] }; \ AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \ { \ #define ON_COMMAND(id, memberFxn) \          { WM_COMMAND, 0, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)memberFxn }, #define END_MESSAGE_MAP() \    {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ }; \#define DECLARE_MESSAGE_MAP() \private: \ static const AFX_MSGMAP_ENTRY _messageEntries[]; \protected: \ static AFX_DATA const AFX_MSGMAP messageMap; \ virtual const AFX_MSGMAP* GetMessageMap() const; \#define BEGIN_MESSAGE_MAP(theClass, baseClass) \ const AFX_MSGMAP* theClass::GetMessageMap() const \    { return &theClass::messageMap; } \ AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \ { &baseClass::messageMap, &theClass::_messageEntries[0] }; \ AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \ { \ #define ON_COMMAND(id, memberFxn) \          { WM_COMMAND, 0, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)memberFxn }, #define END_MESSAGE_MAP() \    {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ }; \

嘿嘿,就這麼幾個宏,就構造出一個訊息數組來。

例四、用C宏,智者思維的火花說了半天了,嘴皮子都幹了,舉個例子大家輕鬆一下——看看人家老外是怎麼用宏的。這個例子摘自《C專家編程》。 根據位元模式構建圖形表徵圖(icon)或者圖形(glyph),是一種小型的位元模式映射於螢幕產生的映像。一個位代表映像上的一個像素。如果一個位被設定,那麼它所代表的像素就是“亮”的。如果一個位被清除,那麼它所代表的像素就是“暗”的。所以,一系列的整數值能夠用於為映像編碼。類似Iconedit這樣的工具就是用於繪圖的,他們所輸出的是一個包含一系列整型數的ASCII檔案,可以被一個視窗程序所包含。它所存在的問題是程式中的表徵圖只是一串十六進位數。在C語言中,典型的16X16的黑白圖形可能如下:

static unsigned short stopwatch[] = {0x07C6,0x1FF7,0x383B,0x600C,0x600C,0xC006,0xC006,0xDF06,0xC106,0xC106,0x610C,0x610C,0x3838,0x1FF0,0x07C0,0x0000};

  正如所看到的那樣,這些C語言常量並未有提供有關圖形實際模樣的任何線索。這裡有一個驚人的#define定義的優雅集合,允許程式建立常量使它們看上去像是螢幕上的圖形。

#define X )*2+1#define _ )*2#define s ((((((((((((((((0 /* For building glyphs 16 bits wide */

  定義了它們之後,只要畫所需要的表徵圖或者圖形等,程式會自動建立它們的十六進位模式。使用這些宏定義,程式的自描述能力大大加強,上面這個例子可以轉變為:

static unsigned short stopwatch[] ={s _ _ _ _ _ X X X X X _ _ _ X X _ ,s _ _ _ X X X X X X X X X _ X X X ,s _ _ X X X _ _ _ _ _ X X X _ X X ,s _ X X _ _ _ _ _ _ _ _ _ X X _ _ ,s _ X X _ _ _ _ _ _ _ _ _ X X _ _ ,s X X _ _ _ _ _ _ _ _ _ _ _ X X _ ,s X X _ _ _ _ _ _ _ _ _ _ _ X X _ ,s X X _ X X X X X _ _ _ _ _ X X _ ,s X X _ _ _ _ _ X _ _ _ _ _ X X _ ,s X X _ _ _ _ _ X _ _ _ _ _ X X _ ,s _ X X _ _ _ _ X _ _ _ _ X X _ _ ,s _ X X _ _ _ _ X _ _ _ _ X X _ _ ,s _ _ X X X _ _ _ _ _ X X X _ _ _ ,s _ _ _ X X X X X X X X X _ _ _ _ ,s _ _ _ _ _ X X X X X _ _ _ _ _ _ ,s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _};

  顯然,與前面的代碼相比,它的意思更為明顯。標準的C語言具有八進位、十進位和十六進位常量,但沒有二進位常量,否則的話倒是一種更為簡單的繪製圖形模式的方法。
  如果抓住書的右上方,並斜這看這一頁,可能會猜測這是一個用於流行視窗系統的“cursor busy”小秒錶圖形。我是在幾年前從Usenet comp.lang.c新聞群組學到這個技巧的。

  千萬不要忘了在繪圖結束後清除這些宏定義,否這很可能會給你後面的代碼帶來不可預測的後果。

  水能載舟,亦能覆舟,把握好手中的雙刃劍,讓它好好的為你服務吧,別割破了手。:) 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.