是否有充分的理由始终在C中用括号括起一个定义?

wes*_*ton 48 c parentheses c-preprocessor

显然,有些时候#defines必须有括号,如下:

#define WIDTH 80+20

int a = WIDTH * 2; // expect a==200 but a==120
Run Code Online (Sandbox Code Playgroud)

所以我总是括号,即使它只是一个数字:

#define WIDTH (100)
Run Code Online (Sandbox Code Playgroud)

C的新人问我为什么这样做,所以我试图找到一个边缘情况,在一个数字上没有括号#define会导致问题,但我想不出一个.

这种情况是否存在?

Ale*_*ler 38

是的.预处理器连接operator(##)将导致问题,例如:

#define _add_penguin(a) penguin ## a
#define add_penguin(a) _add_penguin(a)

#define WIDTH (100)
#define HEIGHT 200    

add_penguin(HEIGHT) // expands to penguin200
add_penguin(WIDTH)  // error, cannot concatenate penguin and (100) 
Run Code Online (Sandbox Code Playgroud)

对于stringization(#)也是如此.很明显,这是一个极端情况,考虑到WIDTH可能会如何使用可能无关紧要.尽管如此,仍需要牢记预处理器.

(为什么加入第二企鹅失败的原因是在C99预处理规则微妙的细部- IIRC它失败,因为串联到两个非占位符的预处理标记必须始终导致单个预处理标记-但这是无关紧要的,即使级联被允许它仍会给出与未被括号内的不同的结果#define!).

所有其他响应都是正确的,只要从C++扫描器的角度来看无关紧要,因为实际上,数字是原子的.但是,在我阅读这个问题时,没有任何迹象表明只考虑没有进一步预处理器扩展的情况,所以其他答案即使我完全同意其中包含的建议也是错误的.

  • 从技术上讲,我问过没有括号的问题.这是由括号的存在引起的问题.然而,这比"没有区别"更好. (4认同)
  • @Lundin对你有好处.但是很多人使用它而*需要它.Boost for instances可以很好地利用无法替换的令牌粘贴.我已经在图书馆中需要它,我知道我的案例没有解决方法. (4认同)

asv*_*kau 27

有时您必须编写代码而不是考虑当前的警告,但是下次将要编辑的警告 .

现在你的宏是一个整数.想象一下将来有人编辑它.让我们说他们不是你,而是一个不那么小心或更匆忙的人.括号用于提醒他们在括号中进行任何修改.

这种想法在C中是一个好习惯.我个人以一种人们可能会发现"冗余"的方式编写代码,这类似的东西,尤其是错误处理方面.冗余用于将来编辑的可维护性和可组合性.

  • 如果有人知道非平凡的宏需要括号,他会在更改时添加它们.如果没有,无论你做什么,他都会制造一团糟.所以我反对添加明显不需要的括号.但这是一个意见问题. (4认同)
  • 我不同意 - 你不应该担心非程序员将来维护你的代码的可能性.围绕宏表达式的括号是一个基本的东西,你应该能够假设每个C程序员都知道它.否则通过使用相同的参数,你应该在_everything_周围加上括号:`int x = y + z;`(不是宏)应该使用相同的有缺陷的逻辑总是写成`int x =(y + z);`如果一个有压力的非程序员将来会维护代码,提醒他们运营商优先权的危险. (3认同)
  • 好吧,伙计们,那么我不同意你们的不同意见。:-) 特别是对于 @Lundin 的评论,我不是在谈论非程序员,而是在谈论糟糕或不细心的程序员——这些程序员似乎成群结队地存在,在某些工作环境中,你无法控制谁会维护你的代码线。但即使你有一群优秀的程序员,也许括号不是最好的例子,但我坚信添加更多的行和字符通常可以使编辑更加“可组合” - 我们不应该总是努力编写尽可能短的代码只是因为我们可以。 (3认同)

Bla*_*iev 8

每当define由单个标记(仅一个操作数,没有运算符)组成时,就不需要括号,因为100当lexing和解析时,单个标记(例如)是不可分割的原子.


Nic*_*tti 8

正如Blagovest Buyukliev所说:

define由单个标记(仅一个操作数,无操作符)组成,不需要括号,因为单个标记(例如100)在进行lexing和解析时是不可分割的原子.

但是在宏方面我会推荐以下规则:

  1. 避免像宏这样的函数@see Lundin的评论.

如果你想使用像宏这样的函数严重考虑以下2条规则:

  1. 始终在宏中使用括号作为参数
  2. 仅使用一次宏参数

为什么要统治1?(保持操作顺序正确)

#define quad(x) (x*x)
int a = quad(2+3);
Run Code Online (Sandbox Code Playgroud)

将扩展到:

int a = (2+3*2+3);
Run Code Online (Sandbox Code Playgroud)

为什么要统治2?(确保副作用仅应用一次)

#define quad(x) (x*x)
int i = 1;
int a = quad(i++);
Run Code Online (Sandbox Code Playgroud)

将扩展到:

int a = i++ * i++;
Run Code Online (Sandbox Code Playgroud)


NPE*_*NPE 6

由于100是一个令牌,我怀疑你会发现括号内容很重要的角落情况(对于一个令牌!)

它仍然是IMO的一个好习惯,因为当涉及多个令牌时它们会很重要.


Lig*_*ica 6

没有.没有任何案例#define WIDTH 100可以产生明确或"令人惊讶"的扩张.那是因为它只能导致单个令牌被单个令牌替换.

如您所知,当单个令牌(例如WIDTH)导致多个令牌(例如80 + 20)时,会出现宏观混淆.据我所知,这是在替换中使用括号的唯一原因,正如我在第一段中所探讨的那样,它不适用于此.

但是,抛开这个技术事实,它仍然是一个很好的做法.它可以促进习惯,如果宏被修改为更复杂的东西,它也可以作为提醒.


chu*_*ica 5

当代码只定义一个数字时,@ Alexander Gessler很好地回答了这个问题。

但是,许多编码人员在以下情况中并未注意到一元运算符

#define TEMPERATURE1M (-1)
#define TEMPERATURE1P (+1)
Run Code Online (Sandbox Code Playgroud)

当代码使用使用#define运算符的a时,封闭可()确保预期的数值结果和优先级。

#define TEMPERATURE_WITH  (-1)
#define TEMPERATURE_WITHOUT -1

// Consider how these will compile
int w  = 10-TEMPERATURE_WITH;
int wo = 10-TEMPERATURE_WITHOUT;  // May not compile
Run Code Online (Sandbox Code Playgroud)

给定C99语义更改@Olaf,最后一行代码可以编译