3

#define我对我们不应该在参数中有空格的概念感到困惑。

我认为唯一的限制是我们不应该在宏名称和直接(括号之间有空格。我对么?我不应该在()括号内放置空格吗?

下面的记号是否正确

#define AVERAGE_NUMS( min_val, max_val ) ((min_val + max_val) / 2)

伙计们,我上面的#define C++ 语句只是一个例子。在使用#define 时,我实际上很关心空格。无论如何感谢您的回答。

4

4 回答 4

6

是的,这是正确的。但是您应该将每个参数括在括号中

#define AVERAGE_NUMS( min_val, max_val ) (((min_val) + (max_val)) / 2)

以避免运算符优先级问题。

于 2013-02-24T12:05:30.013 回答
4

你是对的,宏名称和紧随其后的左括号之间没有空格。其他任何地方的空格==很好。

请注意用于宏参数的名称,如果您有一个与参数同名的变量,则会发生奇怪的事情......例如在您的示例中,您的参数是max_val,但您输入Max_val了宏,所以如果有一个变量Max_val你碰巧使用了这个宏,代码会编译得很好,但不能在其他地方编译......

就我个人而言,我总是在宏名称上加上前缀 _ 来帮助避免这种情况......但纯粹主义者告诉我下划线是保留的,不应该使用,而是填充它们:)

于 2013-02-24T12:06:17.613 回答
3

在 C中,当使用宏进行类似的小型计算时,您应该始终将表达式中的每个参数放在括号中,例如:

#define AVERAGE_NUMS(min_val, max_val) (((min_val) + (max_val)) / 2)

宏参数列表中的空格是可选的,因此它们不会“伤害”。正如您已经说过的,左括号前的空格会改变宏的含义:它将不接受任何参数,并用您想要的参数列表和“表达式”替换它的出现。

如果您不将表达式中的参数放在括号中,您可能会遇到奇怪的结果,因为运算符优先级可能会更改您的表达式。这是因为宏只是文本替换规则,因此不尊重编程语言 (C) 的任何内容。

一个失败的小例子(这是一个奇怪的例子,我承认,但是你可以将其他函数写成宏,但“正常”用法会失败):

AVERAGE_NUMS(1 << x, y)

使用您的宏定义,这将扩展为

((1 << x + y) / 2)           // Operator precedence:  1 << (x + y)

但是使用上面的宏定义,它将扩展为

(((1 << x) + (y)) / 2)       // Operator precedence:  (1 << x) + y

在 C++中,我强烈建议您不要使用宏,除非您真的必须这样做。

在不需要指定类型的情况下计算两个数字的平均值的更好方法是使用模板方法:

template<typename T>
T average_nums(T min_val, T max_val) {
    return (min_val + max_val) / T(2);
}

如果您关心性能,您应该注意所有现代编译器都以与宏定义相同的方式处理这段代码,即它们内联代码。这意味着不涉及函数调用,但表达式avg(a, b)被替换为(a + b) / 2.

this 和宏之间的区别在于宏只是在预编译期间发生的文本替换,因此实际的编译步骤将看到类似于(a + b) / 2而不是avg(a, b).

于 2013-02-24T12:05:43.230 回答
3

我相信以下 1999 年 C 标准的摘录涵盖了如何在宏中处理空格的问题,它们确实指出空格只不过是一个标记分隔符,除了我们区分类对象宏的情况和类似函数的宏,基于宏名称和紧随其后的左括号之间是否有空格。

5.1.1.2 翻译阶段

...

1.3The source file is decomposed into preprocessing tokens and sequences of white-space characters (including comments). ... Each comment is replaced by one space character. New-line characters are retained. Whether each nonempty sequence of white-space characters other than new-line is retained or replaced by one space character is implementation-defined.

1.4Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed. ... All preprocessing directives are then deleted.

1.7White-space characters separating tokens are no longer significant.每个预处理令牌都转换成一个令牌。生成的标记在句法和语义上进行分析,并作为翻译单元进行翻译。

...

6.4 词汇元素

句法

令牌:

  • 关键词
  • 标识符
  • 持续的
  • 字符串字面量
  • 标点符号

preprocessing-token:

  • 标题名称
  • 标识符
  • pp号
  • 字符常量
  • 字符串字面量
  • 标点符号
  • 每个不能是上述之一的非空白字符

语义

…… Preprocessing tokens can be separated by white space; this consists of comments (described later), or white-space characters (space, horizontal tab, new-line, vertical tab, and form-feed), or both. As described in 6.10, in certain circumstances during translation phase 4, white space (or the absence thereof) serves as more than preprocessing token separation._

6.10 预处理指令

句法

...

  • # 定义标识符替换列表换行
  • # define identifier lparen identifier-listopt ) replacement-list new-line
  • # define identifier lparen ... ) replacement-list new-line
  • # define identifier lparen identifier-list , ... ) replacement-list new-line

lparen:

  a ( character not immediately preceded by white-space

6.10.3 宏替换

约束

3There shall be white-space between the identifier and the replacement list in the definition of an object-like macro.

语义

10 形式的预处理指令

  • # define identifier lparen identifier-listopt ) replacement-list new-line
  • # define identifier lparen ... ) replacement-list new-line
  • # define identifier lparen identifier-list , ... ) replacement-list new-line

定义一个function-like带参数的宏,在语法上类似于函数调用。

于 2013-02-24T12:40:02.277 回答