C语言中宏定义的良好编程实践(#define)

Sri*_*nth 24 c c-preprocessor

例如,永远不要像这样定义一个宏:

#define DANGER 60 + 2
Run Code Online (Sandbox Code Playgroud)

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

int wrong_value = DANGER * 2; // Expecting 124
Run Code Online (Sandbox Code Playgroud)

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

#define HARMLESS (60 + 2)
Run Code Online (Sandbox Code Playgroud)

这个例子很简单,但这几乎解释了我的问题.在编写宏时,您会建议使用哪些指南或最佳实践吗?

谢谢你的时间!

unw*_*ind 29

在执行一个运行其参数并且表现得像表达式的宏时,这是惯用的:

 #define DOIT(x) do { x } while(0)
Run Code Online (Sandbox Code Playgroud)

该表格具有以下优点:

  1. 它需要一个终止分号
  2. 它适用于嵌套和大括号,例如if/else


Rod*_*ddy 29

你不仅应该在参数周围放置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
Run Code Online (Sandbox Code Playgroud)

但是,MIN(3,i++)仍然破碎......

最好的规则是只有在没有其他方法可行时才使用#defines! 我知道你在问C而不是C++,但仍记得他的想法.


Rob*_*ble 10

用括号围绕整个宏观围绕在扩展列表中提到的每个参数:

#define MAX(x, y) ((x) > (y) ? (x) : (y))
Run Code Online (Sandbox Code Playgroud)

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

MAX(a++, b);
Run Code Online (Sandbox Code Playgroud)

a++如果a大于,则评估两次b.


使用宏的大写名称来表明它是一个宏而不是一个函数,因此可以相应地考虑差异(另一个一般的好习惯是不传递对函数有副作用的参数).


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

#define pint int *
Run Code Online (Sandbox Code Playgroud)

因为当有人打字时,它不会按预期运行

pint a, b;
Run Code Online (Sandbox Code Playgroud)

请改用typedef.


Joh*_*ing 6

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

static const int DANGER = 60 + 2;
Run Code Online (Sandbox Code Playgroud)


Evi*_*ach 5

在扩展中,在括号周围加上括号,这样如果它们传入一个表达式,你就会得到预期的行为.

#define LESS_THAN(X,Y) (((X) < (Y) ? (X) : (Y))
Run Code Online (Sandbox Code Playgroud)


dal*_*lle 5

对MAX / MIN宏的响应,该宏来自Linux内核中的GCC黑客

#define min(x, y) ({                       \
        typeof(x) _min1 = (x);             \
        typeof(y) _min2 = (y);             \
        (void) (&_min1 == &_min2);         \
        _min1 < _min2 ? _min1 : _min2; })
Run Code Online (Sandbox Code Playgroud)

  • “(({...; foo;})的使用”是另一个GCC扩展。这就是为什么它是GCC hack,而不是C hack。不知道还有什么,但是由于无效,您将停止使用一个有符号和一个无符号参数。x和y的类型兼容,但它们的指针类型不兼容。 (3认同)
  • typeof是gcc扩展,不应在可移植代码中使用。 (2认同)