程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 宏的使用,使用

宏的使用,使用

編輯:C++入門知識

宏的使用,使用


前言

宏在C/C++中有挺大的作用。

關鍵詞

 

宏的幾種作用

1,定義常量

2,定義表達式

3,簡化繁瑣的代碼

4,作為標識符

5,可變參數

6,#和##的使用

 

1,定義常量

比如定義PI的值可以通過宏來定義

#define PI    3.1415927

2,定義表達式

#define MAX(a, b)    (a>b?a:b)

3,簡化繁瑣的代碼

這個可以簡化一些重復的代碼,比如聲明函數,定義類的成員變量,或者是簡化多次編寫重復性高的代碼。

簡化函數聲明,在函數聲明的時候有些必要的關鍵字需要寫,但是很多時候都是一樣的,可以通過宏來簡化

定義線程函數

unsigned __stdcall ThreadFunc(void* pArguments)

可以通過宏簡化為

#define THREAD_FUNC(func)    unsigned __stdcall func(void* pArguments)

THREAD_FUNC(ThreadFunc)
{
    printf("hello\n");
    return 0;
}

 

定義類的成員變量,可能需要定義成員變量的get,set函數,這時候可以通過宏來簡化這個過程。

#define PROP_DECL(ClassName, Prop)    \
    public: ClassName Get##Prop(void){return m_##Prop;}\
    public: void Set##Prop(ClassName vl){m_##Prop = vl;}\
    private: ClassName m_##Prop;

class CTestObj
{
    PROP_DECL(int, Count)
public:
    CTestObj();
    ~CTestObj();
};

上面的代碼經過預編譯之後就會產生GetCount和SetCount兩個函數和m_Count私有成員變量。

簡化繁瑣代碼,在內存釋放的時候可能需要把指針,需要兩行代碼

WA *pa = new WA(c);
delete pa;
pa = NULL;

可以使用宏來簡化這個過程

#define MEM_FREE(x)    do \
{\
    delete x;\
    x = NULL;\
} while (0)

WA *pa = new WA(c);
MEM_FREE(pa);

在ATL,MFC中大量使用到宏來簡化代碼。

4,作為標識

作為標識的宏在大量的使用

#ifndef __TMP_H__    //判斷是否已經定義宏,如果沒有,將會執行下面代碼,用於避免包含文件的時候重復包含
#define __TMP_H__    //定義宏,這樣第二次包含這個頭文件的時候就不會執行下面的定義

//判斷是否是UNICODE,用於定義TTCHAR的字符類型。
#ifdef UNICODE        
typedef wchar_t TTCHAR;
#else
typedef char TTCHAR;
#endif

//用於標識的宏定義
#define    IN
#define OUT

#endif // !__TMP_H__

 

4,可變參數

宏可以有參數,而且參數數量可以不定

#define LOG(format, ...)    printf(format, __VA_ARGS__)

LOG("hello, %d, %s\n", 10, "nihao");

5,#和##的使用

#的作用是把宏參數變成字符串

#define STR(s)     #s
printf(STR(hello));           // 輸出字符串"hello"

##的作用是把宏參數粘貼起來例子可以參考一下第三點的代碼。

 

使用宏需要注意的點

宏是在預處理過程中進行存文本替換,預處理過程中不會對宏進行任何的語法檢測,卻個括號,少個分號都不會管,所以可能會出現一些很奇怪的錯誤,所以要慎用。

1,算法優先問題

一個乘法的宏

#define MUL(x, y)    (x * y)

MUL(2,3)的時候沒有問題,如果是MUL(1+2, 3)的時候就出事了,宏會替換成1+2*3,跟預想的結果就不一樣了。

這時候就需要把宏定義改成

#define MUL(x, y) (x) * (y)

2,分號吞噬問題

#define SKIP_SPACES(p, limit)  \
     { char *lim = (limit);         \
       while (p < lim) {            \
         if (*p++ != ' ') {         \
           p--; break; }}}

如果上面的代碼放在判斷語句中使用

if (*p != 0)
   SKIP_SPACES (p, lim);
else ...

編譯器會報錯,else之前要有if,可以通過下面代碼來解決

#define SKIP_SPACES(p, limit)     \
     do { char *lim = (limit);         \
          while (p < lim) {            \
            if (*p++ != ' ') {         \
              p--; break; }}}          \
     while (0)

這種方式在linux中大量使用到

 

3,重復調用

#define min(X, Y)  ((X) < (Y) ? (X) : (Y))

如果調用時這樣

int x = 1;
int y = 2;
min(x++, y);

展開後x++會被調用兩次,這種方式要避免。

 

4,對自身的遞歸引用

有如下宏定義:

#define foo (4 + foo)

按前面的理解,(4 + foo)會展開成(4 + (4 + foo),然後一直展開下去,直至內存耗盡。但是,預處理器采取的策略是只展開一次。也就是說,foo只會展開成(4 + foo),而展開之後foo的含義就要根據上下文來確定了。

對於以下的交叉引用,宏體也只會展開一次。

#define x (4 + y)
#define y (2 * x)

x展開成(4 + y) -> (4 + (2 * x)),y展開成(2 * x) -> (2 * (4 + y))。

注意,這是極不推薦的寫法,程序可讀性極差。

 

5,宏參數預處理

宏參數中若包含另外的宏,那麼宏參數在被代入到宏體之前會做一次完全的展開,除非宏體中含有#或##。

有如下宏定義:

#define AFTERX(x) X_ ## x
#define XAFTERX(x) AFTERX(x)
#define TABLESIZE 1024
#define BUFSIZE TABLESIZE

AFTERX(BUFSIZE)會被展開成X_BUFSIZE。因為宏體中含有##,宏參數直接代入宏體。
XAFTERX(BUFSIZE)會被展開成X_1024。因為XAFTERX(x)的宏體是AFTERX(x),並沒有#或##,所以BUFSIZE在代入前會被完全展開成1024,然後才代入宏體,變成X_1024。

 

宏實在預處理過程中被替換掉的,所以在實際的編譯過程中,不會出現宏,或者宏的變量名。

 

參考:

http://hbprotoss.github.io/posts/cyu-yan-hong-de-te-shu-yong-fa-he-ji-ge-keng.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved