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

发布于 2024-07-10 02:53:21 字数 1449 浏览 5 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

戈亓 2024-07-17 02:53:21

当执行一个运行其参数并像表达式一样运行的宏时,这是惯用的:

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

这种形式具有以下优点:

  1. 它需要一个终止分号
  2. 它可以与嵌套和大括号一起使用,例如与 if/else 一起使用

When doing a macro that is to run its argument and behave like an expression, this is idiomatic:

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

This form has the following advantages:

  1. It needs a terminating semicolon
  2. It works with nesting and braces, e.g. with if/else
把回忆走一遍 2024-07-17 02:53:21

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

#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.

月朦胧 2024-07-17 02:53:21

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

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

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

MAX(a++, b);

如果 a 大于 b,则会对 a++ 求值两次。


对宏使用大写名称,以明确它是宏而不是函数,以便可以相应地考虑差异(另一个通用的良好实践是不传递对函数有副作用的参数)。


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

#define pint int *

时,它不会按预期运行。

pint a, b;

因为当有人键入Use typedefs

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

pint a, b;

Use typedefs instead.

无远思近则忧 2024-07-17 02:53:21

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

static const int DANGER = 60 + 2;

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.

static const int DANGER = 60 + 2;
So尛奶瓶 2024-07-17 02:53:21

在扩展中,在参数两边加上括号,这样如果它们传入一个表达式,您将获得预期的行为。

#define LESS_THAN(X,Y) (((X) < (Y) ? (X) : (Y))

In the expansion, put parenthesis around the arguments, so that if they pass in a expression you will get the intended behavior.

#define LESS_THAN(X,Y) (((X) < (Y) ? (X) : (Y))
天煞孤星 2024-07-17 02:53:21

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

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

Response to the MAX/MIN macros, taken from 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; })
盛夏已如深秋| 2024-07-17 02:53:21

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

#define MAX 10

很容易与其他代码发生冲突,所以:

#define MYPROJECT_MAX 10

或者更独特的名称会更好。

我见过这样的情况,这种冲突不会产生编译错误,但会生成稍微错误的代码,因此它可能非常阴险。

Use fairly unique names for your macros, since they have global scope and can clash with anything, so:

#define MAX 10

could easily clash with other code, so:

#define MYPROJECT_MAX 10

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.

对岸观火 2024-07-17 02:53:21

对于多行宏,请使用 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)

For multiple line macros, use a do { } while (0):

#define foo(x) do {  \
    (x)++;           \
    printf("%d", x); \
} while(0)

Had you done

#define foo(x) {     \
    (x)++;           \
    printf("%d", x); \
}

instead,

if (xyz)
    foo(y);
else
    foo(z);

would've failed.

Also, be careful when introducing temporary variables in macros:

#define foo(t) do {    \
    int x = (t);       \
    printf("%d\n", x); \
} while(0)

int x = 42;
foo(x);

will print 0 and not 42.

If you have a complicated expression which needs to return a value, you can use the comma operator:

#define allocate(foo, len) (foo->tmp = foo->head, foo->head += len, foo->tmp)
把时间冻结 2024-07-17 02:53:21

取消宏定义。

您的#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.

半透明的墙 2024-07-17 02:53:21

如果您细心且专业,则可以通过使用宏作为简单的代码生成器来完成 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 行编辑。

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.

时光清浅 2024-07-17 02:53:21

看我多么讨厌这个:

void bar(void) {
    if(some_cond) {
        #define BAZ ...
        /* some code */
        #undef BAZ
    }
}

总是这样放置它们:

void bar(void) {
    if(some_cond) {
#define BAZ ...
        /* some code */
#undef BAZ
    }
}

See how I hate this:

void bar(void) {
    if(some_cond) {
        #define BAZ ...
        /* some code */
        #undef BAZ
    }
}

Always put them like this:

void bar(void) {
    if(some_cond) {
#define BAZ ...
        /* some code */
#undef BAZ
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文