【问题标题】:Stopping macro expansion停止宏扩展
【发布时间】:2015-02-16 17:31:50
【问题描述】:

我对宏扩展延迟有疑问。这是一个例子:

#include <stdio.h>

#define CONST_ABC 15
#define CONST_5 7
#define ABC 5

#define PRINT(x) printf("CONST=%d\n", CONST_ ## x)

// The problematic macro
#define PRINT2(x) PRINT(x)

int main(int argc, char *argv[])
{
    PRINT(ABC); // Prints 15 - OK
    PRINT2(ABC); // Prints 7 - Not OK.
}

如何定义PRINT2 宏以便它使用PRINT 并且结果为15?我得到:

CONST=15
CONST=7

又想得到:

CONST=15
CONST=15

【问题讨论】:

  • 如果PRINT 给你想要的结果,你为什么不直接使用它呢?
  • @rici 因为这是一个最小的例子。还有其他参数和更复杂的逻辑。
  • 很公平,但基本思想是一样的。使用双重宏间接导致x 被重新扩展,不要做双重间接。 (将PRINT2 定义为PRINT(CONST_ ## x) 可能会起作用,明显更改为PRINT。)没有看到更接近实际问题的东西,很难知道还能说什么。
  • @rici 我必须使用一个宏。我什至不应该知道它在内部做了什么。所以我需要传递给定的参数(如果可能的话)。所以问题用一种简单的方式来描述:我得到了一个字面量,我必须将它作为字面量传递而不进行扩展。

标签: c c-preprocessor


【解决方案1】:

它要求您至少有一个 C99 编译器,因为 C99 允许 空宏参数。但是,即使在 C89 模式下,某些编译器也可能允许它们作为扩展。代码如下:

#include <stdio.h>

#define CONST_ABC 15
#define CONST_5 7
#define ABC 5

#define PRINT(x) printf("CONST=%d\n", CONST_ ## x)

// The problematic macro
#define PRINT2(x, y) PRINT(x ## y)

int main(int argc, char *argv[])
{
    PRINT(ABC); // Prints 15 - OK
    PRINT2(ABC,); // Prints 7 - Not OK.
}

第二个参数(即y)为空,使其成为空的预处理令牌## 运算符防止参数扩展,因此连接的结果与 x 参数相同。

C11 6.10.3.1/p1 参数替换(强调我的):

在调用类函数宏的参数之后 被识别,参数替换发生。中的一个参数 替换列表,除非前面有 ### 预处理令牌或 后跟一个## 预处理令牌(见下文),被替换为 在其中包含的所有宏都已被处理后的相应参数 扩大。在被替换之前,每个参数的预处理 标记被完全宏替换,就好像它们形成了其余的 预处理文件;没有其他可用的预处理令牌。

【讨论】:

  • @gregorz:希望对您有所帮助。
  • 是否可以用某种形式的定义替换第二个参数?
  • @ValeriAtamaniouk:我不确定你的意思。第二个参数只需要防止前者被替换。请注意,PRINT2(ABC,) 实际上与 PRINT2(,ABC) 相同。
  • 好的。很公平。尽管由于代码审查问题,我无法使用它。不幸的是,定义一个不应使用的宏参数不会通过。 :(
  • @ValeriAtamaniouk:我明白你的意思。不,这是不可能的,因为## 阻止了第二个参数的扩展。那时任何#define 指令都将毫无用处。例如#define EMPTY,然后PRINT2(ABC, EMPTY); 生成一个令牌ABCEMPTY,我相信这不是你想要的。类似函数的宏也是如此。它们没有展开,因为##
【解决方案2】:

宏替换基本上如下进行:

  1. 找到一个令牌,这是一个宏名称
  2. 宏的参数被收集
  3. 实参替换宏定义主体中的形参
  4. 因此被替换的参数完全被宏替换,不包括输入的其余部分;在这个阶段也可以进行字符串化;
  5. 执行标记粘贴操作
  6. 因此被替换的序列被重新扫描,连同输入的其余部分一起用于进一步的宏替换

(加上一些在禁止替换宏时定义不明确的规则)

防止宏参数在 4 中被宏替换的唯一方法。 是因为它后面或前面有一个标记粘贴运算符 (##)。

但是,在 5. 中,粘贴操作员必须使用 正在讨论的论点和一个特殊的地标标记。仅为空参数替换插入地标标记。

检查一下,它可能会让您对真实代码有所了解:

#define PRINT2(noreplace,x) PRINT(noreplace ## x)

PS。是的,“noreplace”应该是空的:)

PRINT2(,ABC)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-12
    • 1970-01-01
    • 2012-08-01
    相关资源
    最近更新 更多