例如,從來沒有定義一個宏是這樣的:良好的編程習慣在C宏定義(#定義)
#define DANGER 60 + 2
這有可能是危險的,當我們做這樣的操作:
int wrong_value = DANGER * 2; // Expecting 124
取而代之的是,像這樣的,因爲你不知道怎麼宏的用戶可以使用它:
#define HARMLESS (60 + 2)
的例子我這很簡單,但這很好的解釋了我的問題。編寫宏時,您會推薦哪些準則或最佳實踐?
謝謝你的時間!
例如,從來沒有定義一個宏是這樣的:良好的編程習慣在C宏定義(#定義)
#define DANGER 60 + 2
這有可能是危險的,當我們做這樣的操作:
int wrong_value = DANGER * 2; // Expecting 124
取而代之的是,像這樣的,因爲你不知道怎麼宏的用戶可以使用它:
#define HARMLESS (60 + 2)
的例子我這很簡單,但這很好的解釋了我的問題。編寫宏時,您會推薦哪些準則或最佳實踐?
謝謝你的時間!
不僅要把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++,但還是要記住他的想法。爲您的宏
當執行宏是運行它的參數和行爲像的表達,這是慣用的:
#define DOIT(x) do { x } while(0)
這種形式具有以下優點:
如果`x`只是一個單獨的陳述呢?例如`foobar = 42`,我們是否仍然需要將它放在`do {} while(0)`中,或者放在圓括號內就足夠了? – 2011-09-03 04:10:49
如果你想要1)宏**請求**終止分號,並且2)將它用作單行if語句中的獨立指令,則您仍然需要它。 – 2011-11-05 20:46:23
選中此鏈接。它很好地解釋了這一點:http://kernelnewbies.org/FAQ/DoWhile0 – 2011-11-05 20:47:06
使用靜態常量值而不是宏常量值,積分或其他。編譯器通常可以優化它們,並且它們仍然是語言類型系統中的一級公民。
static const int DANGER = 60 + 2;
在標準C中真正起作用嗎?在頭文件中? – Roddy 2008-11-26 15:51:57
@Roddy:不,在頭文件中不能在C中工作 - 請使用枚舉。 – 2008-11-26 17:48:05
使用相當獨特的名字,因爲他們有全球範圍內,可與任何衝突,所以:
#define MAX 10
可以很容易地與其它代碼衝突,所以:
#define MYPROJECT_MAX 10
什麼更獨特,會更好。
我見過這種衝突沒有產生編譯錯誤,但生成了一些錯誤的代碼,所以它可能是非常陰險的。
周圍使用在擴展列表中提到的每個參數整個宏和周圍括號:
#define MAX(x, y) ((x) > (y) ? (x) : (y))
是評價他們的論據多次避免編寫宏。
MAX(a++, b);
將評估a++
兩次,如果a
比b
更大:這樣的宏不會當參數有副作用像預期的那樣。
使用大寫名稱爲宏要清楚它是一個宏並沒有那麼的不同,可以相應的考慮(另一種總體上是好的做法是不及格有副作用的功能或者參數)的功能。
不要使用宏來重命名類型是這樣的:
#define pint int *
因爲預期當有人類型
pint a, b;
使用typedef代替它不會表現。
如果您非常小心且專業,您可以通過使用宏作爲簡單的代碼生成器來完成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行編輯。
應對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; })
看我怎麼恨這個:
void bar(void) {
if(some_cond) {
#define BAZ ...
/* some code */
#undef BAZ
}
}
始終把他們像這樣:
void bar(void) {
if(some_cond) {
#define BAZ ...
/* some code */
#undef BAZ
}
}
取消定義你的宏。
您的#defines
應與#undef
匹配。這可以防止預處理器堵塞並影響意外的代碼段。
對於多線宏,使用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)
這不是一個問題。這是一個聲明。 – BIBD 2008-11-26 15:42:24