宏连接中的有效预处理程序标记

时间:2017-01-17 07:25:40

标签: c gcc macros c-preprocessor

我尝试使用串联预处理器操作符##来理解c中的宏,但我意识到我的令牌有问题。我认为这很容易,但实际上并非如此。

因此,串联用于连接两个令牌以创建新令牌。 例如:连接()int*

我试过

#define foo(x,y) x ## y
foo(x,y)

每当我给它一些论据时,我总是会错误地说pasting both argument does not give a valid preprocessor token.

例如,为什么连接foo(1,aa)会导致1aa哪种类型的令牌?以及为什么有效)但foo(int,*)我收到错误

有没有办法知道哪些令牌有效,或者是否有可能有一些很好的联系,以了解如何在我的脑海中澄清它。 (我已经谷歌搜索谷歌和SO)

我错过了什么?

我将不胜感激。

4 个答案:

答案 0 :(得分:5)

预处理程序标记连接用于生成新标记,但它无法将任意语言结构粘贴在一起(例如,gcc documentation):

  

但是,不能同时形成有效令牌的两个令牌不能   贴在一起。例如,您无法将x与+ in连接   要么订购。

所以尝试使用一个宏来指出一个像

这样的类型的指针
#define MAKEPTR(NAME)  NAME ## *
MAKEPTR(int) myIntPtr;

无效,因为int*是两个令牌,而不是一个。

然而,上述链接的示例显示了新令牌的生成:

 #define COMMAND(NAME)  { #NAME, NAME ## _command }

 struct command commands[] =
 {
   COMMAND (quit),
   COMMAND (help),
   ...
 };

的产率:

 struct command commands[] =
 {
   { "quit", quit_command },
   { "help", help_command },
   ...
 };

令牌quit_command之前不存在,但已通过令牌连接生成。

请注意表单

的宏
#define MAKEPTR(TYPE)  TYPE*
MAKEPTR(int) myIntPtr;

有效且实际上从TYPE生成指针类型,例如int*中的int

答案 1 :(得分:3)

由于它似乎是一个混乱点,字符串1aa是一个有效的预处理器标记;它是pp-number的一个实例,其定义是(当前C标准的第6.4.8节):

     pp-number:
            digit
            . digit
            pp-number       digit
            pp-number       identifier-nondigit
            pp-number       e sign
            pp-number       E sign
            pp-number       p sign
            pp-number       P sign
            pp-number       .

换句话说,pp-number以数字或开头。后跟一个数字,然后它可以包含任何数字序列,“identifier-nondigits”(是,字母,下划线和其他可以成为标识符一部分的东西)或字母 e p (大写或小写)后跟加号或减号。

这意味着,例如,0x1e+2是有效pp-number,而0x1f+1则不是({3}令牌)。在有效的程序中,在预处理阶段中存活的每个pp-number必须满足某些数字常量表示的语法,这意味着包含文本0x1e+2的程序将被视为无效。道德,如果有的话,就是你应该慷慨地利用空白;没有成本。

pp-number的意图是在C的某个未来版本中包含最终可能是数字的所有内容。(请记住,数字后面可以跟有表示类型和签名的字母后缀,例如27LU )。

但是,int*不是有效的预处理程序令牌。它是两个令牌(如-3),因此无法使用令牌连接运算符形成。

令牌粘贴规则的另一个奇怪后果是,无法通过令牌连接生成有效令牌...,因为..不是有效令牌。 (a##b##c必须按某种顺序进行评估,因此即使所有三个预处理器宏都扩展为,也必须尝试创建令牌..,这将失败必须编译器,虽然我相信Visual Studio接受它。)

最后,评论符号/*//不是令牌;在将程序文本分离为标记之前,注释将替换为空格。所以你不能用令牌粘贴产生注释(至少不能在兼容的编译器中)。

答案 2 :(得分:2)

预处理令牌由C语言语法定义,参见当前标准的第6.4节:

preprocessing-token:
                   header-name
                   identifier
                   pp-number
                   character-constant
                   string-literal
                   punctuator
                   each non-white-space character that cannot be one of the above

每个术语的含义在语法的其他地方定义。大多数是不言自明的; identifier表示任何有效的变量名称(或者如果它不是关键字),pp-number包括整数和浮点常量。

在标准C中,粘贴两个预处理标记的结果必须是另一个有效的预处理标记。从历史上看,一些预处理器允许其他粘贴(这相当于不粘贴!)但是当人们使用不同的编译器编译代码时,这会导致混淆。

答案 3 :(得分:0)

首先需要了解什么是C中的token
令牌是程序中最小的元素,对编译器很有意义。

名单如下: 令牌可以分为以下几类:

Keywords
Identifiers
Constants
Strings
Special Symbols
Operators

让我们看看下面的节目

#include <stdio.h>
#define concat(front, back) front ## back
int main() {
    printf("%i\n", concat(123, 456));
}

输出: 123456

以上程序运行时 123456 是 int 类型和一个常量值

让我们修改程序

 #include <stdio.h>
    #define concat(front, back) front ## back
    int main() {
        printf("%s\n", concat(aa, 1));
    }

以上会产生以下错误:

error: ‘aa1’ undeclared (first use in this function)
   47 |     concat(aa, 1)
      |            ^~

aa1 是一个有效的预处理器令牌,也是一个有效的标识符,但没有定义,这就是编译器抱怨“aa1”未声明的原因

为了修复错误,如果我像下面那样定义 aa1

 char aa1[] = "hello World"
  or 
 #define aa1 "Hello World"

然后它就可以正常工作了。

#include <stdio.h>
#define aa1 "Hello World"
#define concat(front, back) front ## back
int main() {
    printf("%s\n", concat(aa, 1));
}

输出:Hello World

现在尝试 concat(aa1, aa1),它仍然会产生有效的预处理器令牌和有效的标识符,但 aa1aa1 未定义

try concat(, aa1) // 假设定义了 aa1

希望以上解释有帮助。