#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++) 仍然被破坏...
最好的规则是仅在没有其他方法有效时才使用#defines!我知道你是询问 C 而不是 C++,但仍然记住他的。
Not only should you put parens around the arguments, you should put parens around the expression returned.
#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
However, MIN(3,i++) is still broken...
The best rule is only to use #defines only when NO OTHER APPROACH WILL WORK! I know you're asking about C instead of C++, but still bear his in mind.
Use parenthesis around the entire macro and around each argument referred to in the expansion list:
#define MAX(x, y) ((x) > (y) ? (x) : (y))
Avoid writing macros that evaluate their arguments multiple times. Such macros will not behave as expected when arguments have side effects:
MAX(a++, b);
Will evaluate a++ twice if a is greater than b.
Use UPPERCASE names for macros to make it clear it is a macro and not a function so that the differences can be considered accordingly (another general good practice is not passing arguments that have side effects to functions either).
Don't use macros to rename types like this:
#define pint int *
because it won't behave as expected when someone types
use static const values instead of macros for constant values, integral or other. The compiler can often optimize them away, and they remain a 1-st class citizen in the language's type system.
// 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 行编辑。
If you're careful and expert, you may be able to accomplish DRY (Don't-Repeat-Yourself) code, by using macros as simple code generators. You do have to explain to other programmers what you're doing, but it can save a lot of code. For example, the list-macro technique:
// 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 ..
Now, if you want to add a new variable, delete one, or rename one, it's a 1-line edit.
发布评论
评论(11)
当执行一个运行其参数并像表达式一样运行的宏时,这是惯用的:
这种形式具有以下优点:
When doing a macro that is to run its argument and behave like an expression, this is idiomatic:
This form has the following advantages:
不仅应该在参数周围放置括号,还应该在返回的表达式周围放置括号。
但是,
MIN(3,i++)
仍然被破坏...最好的规则是仅在没有其他方法有效时才使用#defines!我知道你是询问 C 而不是 C++,但仍然记住他的。
Not only should you put parens around the arguments, you should put parens around the expression returned.
However,
MIN(3,i++)
is still broken...The best rule is only to use #defines only when NO OTHER APPROACH WILL WORK! I know you're asking about C instead of C++, but still bear his in mind.
在整个宏周围使用括号,并在扩展列表中引用的每个参数周围使用括号:
避免编写多次计算其参数的宏。 当参数有副作用时,此类宏将不会按预期运行:
如果
a
大于b
,则会对a++
求值两次。对宏使用大写名称,以明确它是宏而不是函数,以便可以相应地考虑差异(另一个通用的良好实践是不传递对函数有副作用的参数)。
不要使用宏来重命名这样的类型:
时,它不会按预期运行。
因为当有人键入Use typedefs
Use parenthesis around the entire macro and around each argument referred to in the expansion list:
Avoid writing macros that evaluate their arguments multiple times. Such macros will not behave as expected when arguments have side effects:
Will evaluate
a++
twice ifa
is greater thanb
.Use UPPERCASE names for macros to make it clear it is a macro and not a function so that the differences can be considered accordingly (another general good practice is not passing arguments that have side effects to functions either).
Don't use macros to rename types like this:
because it won't behave as expected when someone types
Use typedefs instead.
对于常量值(整数或其他),使用静态常量值而不是宏。 编译器通常可以优化它们,并且它们仍然是语言类型系统中的一等公民。
use static const values instead of macros for constant values, integral or other. The compiler can often optimize them away, and they remain a 1-st class citizen in the language's type system.
在扩展中,在参数两边加上括号,这样如果它们传入一个表达式,您将获得预期的行为。
In the expansion, put parenthesis around the arguments, so that if they pass in a expression you will get the intended behavior.
对 MAX/MIN 宏的响应,取自 GCC hacks Linux 内核:
Response to the MAX/MIN macros, taken from GCC hacks in the Linux kernel:
为宏使用相当独特的名称,因为它们具有全局范围并且可能与任何内容发生冲突,因此:
很容易与其他代码发生冲突,所以:
或者更独特的名称会更好。
我见过这样的情况,这种冲突不会产生编译错误,但会生成稍微错误的代码,因此它可能非常阴险。
Use fairly unique names for your macros, since they have global scope and can clash with anything, so:
could easily clash with other code, so:
or something even more unique, would be better.
I've seen cases where a clash of this kind didn't produce a compile error, but generated slightly wrong code, so it can be quite insidious.
对于多行宏,请使用
do { } while (0)
:如果您
改为这样做,
将会失败。
另外,在宏中引入临时变量时要小心:
将打印
0
而不是42
。如果你有一个复杂的表达式需要返回一个值,你可以使用逗号运算符:
For multiple line macros, use a
do { } while (0)
:Had you done
instead,
would've failed.
Also, be careful when introducing temporary variables in macros:
will print
0
and not42
.If you have a complicated expression which needs to return a value, you can use the comma operator:
取消宏定义。
您的
#defines
应与#undef
匹配。 这可以防止预处理器堵塞并影响意外的代码片段。Undefine your macros.
Your
#defines
should matched with an#undef
. This prevents the preprocessor from getting clogged up and affecting unintended pieces of code.如果您细心且专业,则可以通过使用宏作为简单的代码生成器来完成 DRY(不要重复自己)代码。 您确实必须向其他程序员解释您在做什么,但这可以节省大量代码。 例如,列表宏技术:
现在,如果您想要添加一个新变量、删除一个变量或重命名一个变量,只需进行 1 行编辑。
If you're careful and expert, you may be able to accomplish DRY (Don't-Repeat-Yourself) code, by using macros as simple code generators. You do have to explain to other programmers what you're doing, but it can save a lot of code. For example, the list-macro technique:
Now, if you want to add a new variable, delete one, or rename one, it's a 1-line edit.
看我多么讨厌这个:
总是这样放置它们:
See how I hate this:
Always put them like this: