良好的编程习惯在C宏定义(#定义)

良好的编程习惯在C宏定义(#定义)

问题描述:

例如,从来没有定义一个宏是这样的:良好的编程习惯在C宏定义(#定义)

#define DANGER 60 + 2 

这有可能是危险的,当我们做这样的操作:

int wrong_value = DANGER * 2; // Expecting 124 

取而代之的是,像这样的,因为你不知道怎么宏的用户可以使用它:

#define HARMLESS (60 + 2) 

的例子我这很简单,但这很好的解释了我的问题。编写宏时,您会推荐哪些准则或最佳实践?

谢谢你的时间!

+1

这不是一个问题。这是一个声明。 – BIBD 2008-11-26 15:42:24

不仅要把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 LESS_THAN(X,Y) (((X) < (Y) ? (X) : (Y)) 
+1

thisw有++/- 错误 – dsm 2008-11-26 16:31:14

+0

Ya。不要这样做。 – EvilTeach 2008-11-26 18:21:14

当执行宏是运行它的参数和行为像的表达,这是惯用的:

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

这种形式具有以下优点:

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

如果`x`只是一个单独的陈述呢?例如`foobar = 42`,我们是否仍然需要将它放在`do {} while(0)`中,或者放在圆括号内就足够了? – 2011-09-03 04:10:49

+1

如果你想要1)宏**请求**终止分号,并且2)将它用作单行if语句中的独立指令,则您仍然需要它。 – 2011-11-05 20:46:23

+1

选中此链接。它很好地解释了这一点:http://kernelnewbies.org/FAQ/DoWhile0 – 2011-11-05 20:47:06

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

static const int DANGER = 60 + 2; 
+0

在标准C中真正起作用吗?在头文件中? – Roddy 2008-11-26 15:51:57

+0

@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++两次,如果ab更大:这样的宏不会当参数有副作用像预期的那样。


使用大写名称为宏要清楚它是一个宏并没有那么的不同,可以相应的考虑(另一种总体上是好的做法是不及格有副作用的功能或者参数)的功能。


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

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