嵌套 FIRST_ARG 宏

Nested FIRST_ARG macro

我正在玩 C 宏,但我无法理解下面出现的问题。

#define FIRST_ARG(N, ...)   N

#define FIRST_ARG_EXPANDED(N, ...)   FIRST_ARG(N, __VA_ARGS__)

#define ELEMENTS(DEF, ...)  DEF(1, 2, 3), \
                            DEF(4, 5, 6)

int main()
{
    char array1[] = { FIRST_ARG(ELEMENTS(FIRST_ARG)) };
    char array2[] = { FIRST_ARG_EXPANDED(ELEMENTS(FIRST_ARG)) };
    printf("array1 size = %zu, array2 size = %zu \n", sizeof(array1), sizeof(array2));

    return 0;
}

原来array1array2不一样。 FIRST_ARG(ELEMENTS(FIRST_ARG)) 扩展为 1, 4FIRST_ARG_EXPANDED(ELEMENTS(FIRST_ARG)) 结果为 1

我相信对此有一些解释。看Eclipse一步步展开:

谁能找到基于C标准的解释?

参数首先被宏替换,而不是重新扫描以用逗号分隔它们,因此变成 1, 4 的宏仍然是一个参数。但是,替换宏后,将重新扫描新序列,并以逗号分隔参数。

根据 C 2018 6.10.3.1 1,在替换宏本身之前处理宏的参数以进行宏替换:

… Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.

因此,在 FIRST_ARG(ELEMENTS(FIRST_ARG)) 中,我们首先替换 ELEMENTS(FIRST_ARG),从而生成 FIRST_ARG(1, 2, 3), FIRST_ARG(4, 5, 6)。根据 6.10.3.4 1:

重新扫描此替换以进行进一步替换

… The resulting preprocessing token sequence is then rescanned, along with all subsequent preprocessing tokens of the source file, for more macro names to replace.

重新扫描导致 FIRST_ARG(1, 2, 3), FIRST_ARG(4, 5, 6) 变为 1, 4。因此,处理原始 FIRST_ARG 的参数的结果是 1, 4。请注意,这本身就是整个第一个参数;它不会被重新处理,因此逗号会导致参数的分隔——它会作为 FIRST_ARG 的第一个参数的值。然后 FIRST_ARG 被参数的这个值替换,1, 4.

FIRST_ARG_EXPANDED(ELEMENTS(FIRST_ARG))中,我们再次先替换参数,得到上面的参数1, 4,然后我们有FIRST_ARG_EXPANDED(1, 4)。这将替换为 FIRST_ARG(1, 4)。现在已扫描,1, 4 的标记成为 FIRST_ARG 的单独参数,因此结果是第一个参数,1.

综上所述,替换宏的一个参数产生一个参数。如果要重新扫描它以在逗号处分隔为多个参数,则必须使用另一个宏。