代码之家  ›  专栏  ›  技术社区  ›  Srikanth

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

  •  24
  • Srikanth  · 技术社区  · 16 年前

    例如,切勿定义如下宏:

    #define DANGER 60 + 2
    

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

    int wrong_value = DANGER * 2; // Expecting 124
    

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

    #define HARMLESS (60 + 2)
    

    这个例子很简单,但这基本上解释了我的问题。在编写宏时,您是否会推荐一些指导原则或最佳实践?

    谢谢你的时间!

    11 回复  |  直到 10 年前
        1
  •  30
  •   Roddy    12 年前

    您不仅应该在参数周围放置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++,但还是要记住他。

        2
  •  29
  •   unwind    16 年前

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

    此表格具有以下优点:

    1. 它需要一个终止分号
    2. 它适用于嵌套和大括号,例如if/else
        3
  •  10
  •   Robert Gamble    16 年前

    围绕展开列表中提到的每个参数:

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

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

    MAX(a++, b);
    

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


    对宏使用大写名称,以明确它是一个宏而不是一个函数,这样就可以相应地考虑差异(另一个常见的良好做法是不向函数传递有副作用的参数)。


    不要使用宏重命名以下类型:

    #define pint int *
    

    因为当有人打字时,它的行为不会像预期的那样

    pint a, b;
    

        4
  •  6
  •   John Dibling    16 年前

    对于常量值、整型值或其他值,请使用静态常量值而不是宏。编译器通常可以对它们进行优化,它们仍然是语言类型系统中的第一类公民。

    static const int DANGER = 60 + 2;
    
        5
  •  5
  •   EvilTeach    16 年前

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

    #define LESS_THAN(X,Y) (((X) < (Y) ? (X) : (Y))
    
        6
  •  5
  •   dalle    16 年前

    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; })
    
        7
  •  2
  •   DarthPingu    16 年前

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

    #define MAX 10
    

    #define MYPROJECT_MAX 10
    

    我见过这样的情况,这种冲突不会产生编译错误,但会生成稍有错误的代码,因此它可能相当隐蔽。

        8
  •  2
  •   sanjoyd    14 年前

    对于多行宏,请使用 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)
    
        9
  •  2
  •   Drew Noakes    11 年前

    取消定义宏。

    你的 #defines 应该配上 #undef

        10
  •  1
  •   Mike Dunlavey    16 年前

    如果您细心且专业,则可以通过使用宏作为简单的代码生成器来完成干代码(不要重复自己的代码)。你必须向其他程序员解释你在做什么,但这可以节省很多代码。例如,列表宏技术:

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

    现在,如果您想添加一个新变量、删除一个变量或重命名一个变量,它是一个单行编辑。

        11
  •  0
  •   Peter Hall    7 年前

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

    总是这样说:

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