良好的编程习惯在C宏定义(#定义)
例如,从来没有定义一个宏是这样的:良好的编程习惯在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)
这种形式具有以下优点:
- 它需要一个终止分号
- 它适用于嵌套和大括号,例如与if/else
如果`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