有效和无效的PP代币的定义是什么?
What are the definitions for valid and invalid pp-tokens?
我想广泛使用##运算符和枚举魔法来处理大量类似的访问操作,错误处理和数据流。
如果应用 ##
和 #
预处理器运算符导致 pp 令牌无效,则行为在 C 中未定义。
C90 中通常未定义预处理器操作的顺序 (*((请参阅令牌粘贴运算符(。现在在某些情况下(在不同的来源中这样说,包括MISRA委员会和参考页面(多个##/#-运算符的顺序会影响未定义行为的发生。但我真的很难理解这些来源的例子并确定共同的规则。
所以我的问题是:
-
有效PP代币的规则是什么?
-
不同的C标准和C++标准之间有区别吗?
-
我目前的问题:以下所有 2 个操作员订单是否合法?**)
#define test(A) test_## A ## _THING int test(0001) = 2;
评论:
(*( 我不使用"未定义",因为这与未定义的行为无关,恕我直言,而是未指定的行为。应用多个 ## 或 # 运算符通常不会使程序出错。显然有一个顺序 - 我们无法预测哪个 - 所以顺序没有指定。
(**( 这不是编号的实际应用。但模式是等效的。
有效 pp 代币的规则是什么?
这些在各自的标准中都有详细说明;C11 §6.4 和 C++11 §2.4.在这两种情况下,它们都对应于生产预处理令牌。除了pp编号之外,它们不应该太令人惊讶。其余的可能性是标识符(包括关键字(,"标点符号"(C++,预处理操作或标点(,字符串和字符文字,以及任何与任何其他生产不匹配的非空格字符。
除了少数例外,任何字符序列都可以分解为一系列有效的预处理令牌。(一个例外是不匹配的引号和撇号:单引号或撇号不是有效的预处理标记,因此包含未终止字符串或字符文本的文本不能标记。
但是,在##
运算符的上下文中,串联的结果必须是单个预处理令牌。因此,无效串联是一种串联,其结果是包含多个预处理令牌的字符序列。
C 和 C++ 之间有区别吗?
是的,略有不同:
-
C++具有用户定义的字符串和字符文本,并允许使用"原始"字符串文本。这些文字在 C 中将以不同的方式进行标记化,因此它们可能是多个预处理令牌,或者(在原始字符串文本的情况下(甚至是无效的预处理标记。
-
C++包括符号
::
、.*
和->*
,所有这些符号在C中都被标记为两个标点符号。此外,在C++中,一些看起来像关键字的东西(例如。new
,delete
( 是预处理操作或朋克的一部分(尽管这些符号在两种语言中都是有效的预处理标记(。 -
C 允许十六进制浮点文字(例如。
1.1p-3
(,它们在C++中不是有效的预处理令牌。 -
C++允许在整数文本中使用撇号作为分隔符(
1'000'000'000
(。在 C 语言中,这可能会导致不匹配的撇号。 -
在处理通用字符名称方面存在细微差异(例如。
u0234
(。 -
在C++中,
<::
将被标记为<
,::
除非后面跟着:
或>
。(<:::
和<::>
使用最长匹配规则正常标记。在 C 语言中,最长匹配规则没有例外;<::
始终使用最长匹配规则进行标记化,因此第一个令牌将始终<:
。
连接test_
、0001
和_THING
是否合法,即使连接顺序未指定?
是的,这在两种语言中都是合法的。
test_ ## 0001 => test_0001 (identifier)
test_0001 ## _THING => test_0001_THING (identifier)
0001 ## _THING => 0001_THING (pp-number)
test_ ## 0001_THING => test_0001_THING (identifier)
无效令牌串联的示例有哪些?
假设我们有
#define concat3(a, b, c) a ## b ## c
现在,无论连接顺序如何,以下内容都是无效的:
concat3(., ., .)
..
不是令牌,即使...
是。但是串联必须按某种顺序进行,..
将是必要的中间值;由于这不是单个令牌,因此串联将无效。
concat3(27,e,-7)
在这里,-7
是两个令牌,因此无法连接。
下面是连接顺序很重要的情况:
concat3(27e, -, 7)
如果这是从左到右连接的,则会产生27e- ## 7
,这是两个 pp 数字的串联。但是-
不能与7
连接,因为(如上所述(-7
不是单一的标记。
PP编号到底是什么?
一般而言,pp-number 是标记的超集,可以转换为(单个(数字文字或可能无效。该定义有意宽泛,部分是为了允许(某些(标记串联,部分是为了将预处理器与数字格式的周期性更改隔离开来。确切的定义可以在相应的标准中找到,但在以下情况下,非正式地,令牌是 pp 编号:
- 它以十进制数字或句点(
.
(开头,后跟十进制数字。 - 令牌的其余部分是字母、数字和句点,如果前面有指数符号,可能包括符号字符(
+
、-
(。指数符号可以用两种语言E
或e
;并且自 C99 以来也在 C 语言中P
和p
。 - 在C++中,pp 编号还可以包含(但不以撇号开头(后跟字母或数字。
- 注意:上面,
letter
包括下划线。此外,可以使用通用字符名称(除了在 C++ 中跟随撇号(。
一旦预处理终止,所有 pp 数字都将转换为数字文字(如果可能(。如果无法进行转换(因为令牌与任何数字文本的语法不对应(,则程序无效。
#define test(A) test_## A ## _THING
int test(0001) = 2;
这对于 LTR 和 RTL 评估都是合法的,因为 test_0001
和 0001_THING
都是有效的预处理器令牌。前者是标识符,而后者是 pp 编号;直到编译的后期阶段才检查 pp 编号的后缀正确性;例如,思考 0001u
无符号的八进制文字。
举几个例子来说明评估的顺序确实很重要:
#define paste2(a,b) a##b
#define paste(a,b) paste2(a,b)
#if defined(LTR)
#define paste3(a,b,c) paste(paste(a,b),c)
#elif defined(RTL)
#define paste3(a,b,c) paste(a,paste(b,c))
#else
#define paste3(a,b,c) a##b##c
#endif
double a = paste3(1,.,e3), b = paste3(1e,+,3); // OK LTR, invalid RTL
#define stringify2(x) #x
#define stringify(x) stringify2(x)
#define stringify_paste3(a,b,c) stringify(paste3(a,b,c))
char s[] = stringify_paste3(%:,%,:); // invalid LTR, OK RTL
如果您的编译器使用一致的计算顺序(LTR 或 RTL(,并在生成无效的 pp 令牌时出现错误,那么恰好其中一行将生成错误。当然,宽松的编译器很可能允许两者,而严格的编译器可能两者都不允许。
第二个例子相当人为;由于语法的构造方式,很难找到一个在构建 RTL 时有效但在构建 LTR 时无效的 pp-token 。
在这方面,C 和 C++ 之间没有显着差异;这两个标准具有相同的语言(直到章节标题(描述宏替换过程。语言可能影响该过程的唯一方式是在有效的预处理令牌中:C++(尤其是最近(具有更多形式的有效预处理令牌,例如用户定义的字符串文本。
- C++中"dependent name"的定义是什么?
- Qt - QVector 和模型视图 - 从列表视图获取自定义类的最佳方法是什么?
- 在模板类之外定义友元函数的正确方法是什么?
- Eclipse CDT 不了解方法定义是什么
- 与 I/O 完成端口的"NumberOfConcurrentThreads"相关的"runnable thread"的定义是什么?
- 这个 typedef 定义是什么意思
- 这两段代码的函数定义是什么
- 指向常量数组的指针的正确定义是什么
- 移动平台的预处理器定义是什么?
- xorshift128+算法的真正定义是什么
- typedef Vec<int, 2> Vec2i; 这个定义是什么意思?
- 常量正确性的定义是什么?
- "variable"的定义是什么?
- 声明性区域的实际定义是什么?
- 有效和无效的PP代币的定义是什么?
- 未声明标识符和标识符未定义是什么意思?如何修复错误
- boost::asio::io_service就绪处理程序的定义是什么?
- c++中位操作符的定义是什么?
- c++中与#define KB_UP 72相关的其他定义是什么?它们都是什么?
- 以下结构定义是什么意思