2008-11-26 44 views
23

例如,從來沒有定義一個宏是這樣的:良好的編程習慣在C宏定義(#定義)

#define DANGER 60 + 2 

這有可能是危險的,當我們做這樣的操作:

int wrong_value = DANGER * 2; // Expecting 124 

取而代之的是,像這樣的,因爲你不知道怎麼宏的用戶可以使用它:

#define HARMLESS (60 + 2) 

的例子我這很簡單,但這很好的解釋了我的問題。編寫宏時,您會推薦哪些準則或最佳實踐?

謝謝你的時間!

+1

這不是一個問題。這是一個聲明。 – BIBD 2008-11-26 15:42:24

回答

26

不僅要把parens放在參數上,還要把parens放在返回的表達式上。

#define MIN(a,b) a < b ? a : b  // WRONG 

int i = MIN(1,2); // works 
int i = MIN(1,1+1); // breaks 

#define MIN(a,b) (a) < (b) ? (a) : (b) // STILL WRONG 

int i = MIN(1,2); // works 
int i = MIN(1,1+1); // now works 
int i = MIN(1,2) + 1; // breaks 

#define MIN(a,b) ((a) < (b) ? (a) : (b)) // GOOD 

int i = MIN(1,2); // works 
int i = MIN(1,1+1); // now works 
int i = MIN(1,2) + 1; // works 

然而,MIN(3,i++)仍然是斷開...

最好的規則是隻使用#定義只有在沒有其他辦法將工作!我知道你問的是C而不是C++,但還是要記住他的想法。爲您的宏

5

在展開中,將括號放在參數的周圍,以便如果它們傳遞到表達式中,您將獲得預期的行爲。

#define LESS_THAN(X,Y) (((X) < (Y) ? (X) : (Y)) 
+1

thisw有++/- 錯誤 – dsm 2008-11-26 16:31:14

+0

Ya。不要這樣做。 – EvilTeach 2008-11-26 18:21:14

28

當執行宏是運行它的參數和行爲像的表達,這是慣用的:

#define DOIT(x) do { x } while(0) 

這種形式具有以下優點:

  1. 它需要一個終止分號
  2. 它適用於嵌套和大括號,例如與if/else
+1

如果`x`只是一個單獨的陳述呢?例如`foobar = 42`,我們是否仍然需要將它放在`do {} while(0)`中,或者放在圓括號內就足夠了? – 2011-09-03 04:10:49

+1

如果你想要1)宏**請求**終止分號,並且2)將它用作單行if語句中的獨立指令,則您仍然需要它。 – 2011-11-05 20:46:23

+1

選中此鏈接。它很好地解釋了這一點:http://kernelnewbies.org/FAQ/DoWhile0 – 2011-11-05 20:47:06

6

使用靜態常量值而不是宏常量值,積分或其他。編譯器通常可以優化它們,並且它們仍然是語言類型系統中的一級公民。

static const int DANGER = 60 + 2; 
+0

在標準C中真正起作用嗎?在頭文件中? – Roddy 2008-11-26 15:51:57

+0

@Roddy:不,在頭文件中不能在C中工​​作 - 請使用枚舉。 – 2008-11-26 17:48:05

2

使用相當獨特的名字,因爲他們有全球範圍內,可與任何衝突,所以:

#define MAX 10 

可以很容易地與其它代碼衝突,所以:

#define MYPROJECT_MAX 10 

什麼更獨特,會更好。

我見過這種衝突沒有產生編譯錯誤,但生成了一些錯誤的代碼,所以它可能是非常陰險的。

10

周圍使用在擴展列表中提到的每個參數整個宏周圍括號:

#define MAX(x, y) ((x) > (y) ? (x) : (y)) 

是評價他們的論據多次避免編寫宏。

MAX(a++, b); 

將評估a++兩次,如果ab更大:這樣的宏不會當參數有副作用像預期的那樣。


使用大寫名稱爲宏要清楚它是一個宏並沒有那麼的不同,可以相應的考慮(另一種總體上是好的做法是不及格有副作用的功能或者參數)的功能。


不要使用宏來重命名類型是這樣的:

#define pint int * 

因爲預期當有人類型

pint a, b; 

使用typedef代替它不會表現。

1

如果您非常小心且專業,您可以通過使用宏作爲簡單的代碼生成器來完成DRY(不重複自己)代碼。你必須向其他程序員解釋你在做什麼,但是它可以節省很多代碼。例如,列表宏技術:

// define a list of variables, error messages, opcodes 
// or anything that you have to write multiple things about 
#define VARLIST \ 
    DEFVAR(int, A, 1) \ 
    DEFVAR(double, B, 2) \ 
    DEFVAR(int, C, 3) \ 

// declare the variables 
#define DEFVAR(typ, name, val) typ name = (val); 
    VARLIST 
#undef DEFVAR 

// write a routine to set a variable by name 
void SetVar(string varname, double value){ 
    if (0); 
    #define DEFVAR(typ, name, val) else if (varname == #name) name = value; 
     VARLIST 
    #undef DEFVAR 
    else printf("unrecognized variable %s\n", varname); 
} 

// write a routine to get a variable's value, given its name 
// .. you do it .. 

現在,如果你想添加一個新的變量,刪除一個或重命名一個,這是一個1行編輯。

4

應對MAX/MIN宏,從GCC hacks in the Linux kernel採取:

#define min(x, y) ({      \ 
     typeof(x) _min1 = (x);    \ 
     typeof(y) _min2 = (y);    \ 
     (void) (&_min1 == &_min2);   \ 
     _min1 < _min2 ? _min1 : _min2; }) 
0

看我怎麼恨這個:

void bar(void) { 
    if(some_cond) { 
     #define BAZ ... 
     /* some code */ 
     #undef BAZ 
    } 
} 

始終把他們像這樣:

void bar(void) { 
    if(some_cond) { 
#define BAZ ... 
     /* some code */ 
#undef BAZ 
    } 
} 
2

取消定義你的宏。

您的#defines應與#undef匹配。這可以防止預處理器堵塞並影響意外的代碼段。

1

對於多線宏,使用do { } while (0)

#define foo(x) do { \ 
    (x)++;   \ 
    printf("%d", x); \ 
} while(0) 

假如你做了

#define foo(x) {  \ 
    (x)++;   \ 
    printf("%d", x); \ 
} 

代替,

if (xyz) 
    foo(y); 
else 
    foo(z); 

就已經失敗了。

此外,在宏引入臨時變量的時候要小心:

#define foo(t) do { \ 
    int x = (t);  \ 
    printf("%d\n", x); \ 
} while(0) 

int x = 42; 
foo(x); 

將打印0而不是42

如果您有需要返回一個值,一個複雜的表達式,您可以用逗號:

#define allocate(foo, len) (foo->tmp = foo->head, foo->head += len, foo->tmp)