24

例如,永远不要定义这样的宏:

#define DANGER 60 + 2

当我们执行这样的操作时,这可能会很危险:

int wrong_value = DANGER * 2; // Expecting 124

相反,这样定义是因为您不知道宏的用户如何使用它:

#define HARMLESS (60 + 2)

这个例子很简单,但这几乎解释了我的问题。在编写宏时,您会推荐任何一套指南或最佳实践吗?

谢谢你的时间!

4

11 回答 11

30

当做一个宏来运行它的参数并且表现得像一个表达式时,这是惯用的:

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

这种形式有以下优点:

  1. 它需要一个终止分号
  2. 它适用于嵌套和大括号,例如 if/else
于 2008-11-26T15:43:26.357 回答
30

不仅应该在参数周围加上括号,还应该在返回的表达式周围加上括号。

#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++,但仍然牢记他。

于 2008-11-26T15:49:40.713 回答
10

在整个宏扩展列表中引用的每个参数周围使用括号:

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

避免编写多次评估其参数的宏。当参数有副作用时,此类宏将不会按预期运行:

MAX(a++, b);

如果大于 ,将评估a++两次。ab


为宏使用大写名称以明确它是宏而不是函数,以便可以相应地考虑差异(另一个一般的良好做法是也不将具有副作用的参数传递给函数)。


不要使用宏来重命名这样的类型:

#define pint int *

因为当有人键入时它的行为不会像预期的那样

pint a, b;

请改用 typedef。

于 2008-11-26T16:01:35.010 回答
7

使用静态常量值而不是宏来表示常量值、整数或其他值。编译器通常可以将它们优化掉,并且它们仍然是语言类型系统中的一等公民。

static const int DANGER = 60 + 2;
于 2008-11-26T15:43:44.813 回答
5

在展开式中,在参数周围加上括号,这样如果它们传入表达式,您将获得预期的行为。

#define LESS_THAN(X,Y) (((X) < (Y) ? (X) : (Y))
于 2008-11-26T15:42:43.160 回答
5

对 MAX/MIN 宏的响应,取自Linux 内核中的 GCC hacks

#define min(x, y) ({                       \
        typeof(x) _min1 = (x);             \
        typeof(y) _min2 = (y);             \
        (void) (&_min1 == &_min2);         \
        _min1 < _min2 ? _min1 : _min2; })
于 2008-11-26T17:00:36.903 回答
2

为您的宏使用相当独特的名称,因为它们具有全局范围并且可以与任何内容发生冲突,因此:

#define MAX 10

很容易与其他代码发生冲突,因此:

#define MYPROJECT_MAX 10

或者更独特的东西,会更好。

我见过这种冲突没有产生编译错误,但产生了稍微错误的代码的情况,所以它可能非常阴险。

于 2008-11-26T15:52:41.907 回答
2

取消定义你的宏。

#defines应该与#undef. 这可以防止预处理器堵塞并影响意外的代码片段。

于 2009-01-16T17:41:21.170 回答
2

对于多行宏,请使用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)
于 2011-05-04T04:14:49.957 回答
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 行编辑。

于 2008-11-26T16:20:28.980 回答
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
    }
}
于 2008-11-26T17:37:57.327 回答