我尝试使用串联预处理器操作符##来理解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)
我错过了什么?
我将不胜感激。
答案 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
希望以上解释有帮助。