【发布时间】:2016-09-06 06:35:42
【问题描述】:
我在fwts 代码库中看到下面的代码 sn-p:
#define FWTS_CONCAT(a, b) a ## b
#define FWTS_CONCAT_EXPAND(a,b) FWTS_CONCAT(a, b)
#define FWTS_ASSERT(e, m) \
enum { FWTS_CONCAT_EXPAND(FWTS_ASSERT_ ## m ## _in_line_, __LINE__) = 1 / !!(e) }
#define FWTS_REGISTER_FEATURES(name, ops, priority, flags, features) \
/* Ensure name is not too long */ \
FWTS_ASSERT(FWTS_ARRAY_LEN(name) < 16, \
fwts_register_name_too_long);
我的问题是:
-
1234563导致
顺便说一句,
FWTS_CONCAT_EXPAND(a,b)和FWTS_CONCAT(a, b)似乎是重复的,为什么我们需要两个?
1/0 ?
添加 1
根据@Klas Lindbäck的回答,我想通过一个具体的例子来进行宏扩展。
假设我有:
#define M_1 abc
#define M_2 123
那我猜FWTS_CONCAT_EXPAND(M_1,M_2)的展开过程应该是:
FWTS_CONCAT_EXPAND(M_1,M_2)
->
FWTS_CONCAT(abc, 123)
->
abc123
如果我直接申请FWTS_CONCAT(M_1, M_2),会不会扩成这样?
FWTS_CONCAT(M_1, M_2)
->
M_1M_2
->
Bang! M_1M_2 is an invalid symbol!
(如果我错了,请纠正我......)
添加 2
试过gcc -E macroTest.c -o macroTest.i:
(macroTest.c)
#define M_1 abc
#define M_2 123
#define FWTS_CONCAT(a, b) a ## b
#define FWTS_CONCAT_EXPAND(a,b) FWTS_CONCAT(a, b)
FWTS_CONCAT_EXPAND(M_1, M_2)
FWTS_CONCAT(M_1,M_2)
(macroTest.i)
# 1 "macroTest.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "macroTest.c"
abc123
M_1M_2
我想我明白了宏扩展规则的意义。下面是一些相关的概念和引用:
宏参数完全宏扩展在之前 替换到宏体中,除非它们被字符串化或粘贴 与其他代币。替换后,整个宏体, 包括被替换的参数,再次扫描宏 扩大。结果是参数被扫描两次以展开 宏调用它们。
当宏参数与前导“#”一起使用时,预处理器 用实际参数的文字替换它,转换为 字符串常量。
Token Pasting / Token Concatenation:
在扩展时将两个标记合并为一个通常很有用 宏。这称为令牌粘贴或令牌连接。这 '##' 预处理运算符执行令牌粘贴。当一个宏是 展开后,每个“##”运算符两侧的两个标记是 组合成一个标记,然后替换“##”和两个 宏扩展中的原始标记。
所以我的场景的详细流程是这样的:
FWTS_CONCAT_EXPAND(M_1, M_2)
-> FWTS_CONCAT_EXPAND(abc, 123) // M_1, M_2 pre-expanded since FWTS_CONCAT_EXPAND has no ##.
-> FWTS_CONCAT(abc, 123) // FWTS_CONCAT_EXPAND expanded into FWTS_CONCAT
-> abc123 // FWTS_CONCAT expanded
FWTS_CONCAT(M_1,M_2)
-> M_1M_2 //M_1, M_2 are not pre-expanded because of the ## in FWTS_CONCAT
-> DEADEND
【问题讨论】:
-
我认为它应该会导致错误。
-
它很可能会导致编译错误,因为无法计算常量
1/0。 -
谢谢。但我就是不明白为什么要定义
enum类型来引发编译时错误?enum有什么特别之处吗? -
@smwikipedia:除了它的值必须是恒定的(并且有单行错误输出,给出标识符的抱怨)?我已经看到对数组大小进行了类似操作,但这不会(甚至意外地)为该项目分配任何存储空间。
-
enum在这方面没什么特别的;我使用了typedef具有类似技巧的数组。关键是当预处理器不起作用时,我们使用 C 构造执行编译时检查。例如,您不能在#if语句中使用sizeof来测试数组大小。
标签: c