2

我正在学习 digraph 和 trigraph,这是我无法理解的代码。(是的,我承认它非常丑陋。)

这段代码可以编译:

#define _(s) s%:%:s

main(_(_))
<%
    __;
%>t

这段代码也可以编译:

#define _(s) s??=??=s

main(_(_))
<%
    __;
%>

但是,以下两段代码都无法编译:

#define _(s) s%:??=s

main(_(_))
<%
    __;
%>

#define _(s) s??=%:s

main(_(_))
<%
    __;
%>

这确实让我感到困惑:由于前两段代码可以编译,我认为 digraph 和 trigraph 的扩展都发生在宏扩展之前。那么为什么有向图和三线图一起使用时不能编译呢?

4

2 回答 2

2

二合字母和三合字母完全不同。三元组在翻译的第 1 阶段被替换,[见注 1] 在源代码被分离成标记之前。Digraphs 是标记,它们是其他标记的替代拼写,因此它们在源被分成标记之后才有意义。(“digraph”这个词不是很准确;使用它是因为它类似于“trigraph”,但是digraphs包括%:%:由四个字符组成的集合。)

在完成任何标记分析之前, So??=被替换为 a 。#but%:只是一个象征,与 同义#

而且,%:%:是一个与 同义的记号##。但是%:#是两个标记(%:#),这是不合法的,因为字符串化操作符(无论是拼写%:还是#)只能跟一个宏参数。#[见注 2] 如果是三元组替换的结果,它的非法性也不会降低。

正如chqrlie 的回答中的搞笑片段所示,二合字母和三合字母之间的一个重要区别是三合字母也可以在字符串中使用。即使您的键盘没有方括号和八字形,二合字母也允许您编写 C 代码,但它们不能帮助您将这些字符打印出来。


注释(标准引用):

  1. §5.1.1.2,翻译阶段,第 1 段:

    翻译的语法规则之间的优先级由以下阶段指定。

    1. 如有必要,物理源文件多字节字符以实​​现定义的方式映射到源字符集(为行尾指示符引入换行符)。Trigraph 序列被相应的单字符内部表示替换。
  2. §6.10.3.2,# 运算符,第 1 段:

    类函数宏的替换列表中的每个 # 预处理标记应后跟一个参数,作为替换列表中的下一个预处理标记。

于 2016-01-04T00:51:01.577 回答
2

对于学术方面,请查看 rici 有据可查的答案。

在常识方面,除非你已经相当精通 C,否则二合字母和三合字母完全没用,你甚至不应该在这个主题上浪费任何时间。发明它们是为了支持非美国 7 位字符集,这些字符集在 1980 年代仍在大型机和一些小型计算机上使用。这些字符集缺少 C 语言所需的一些标点符号,例如#,等{}以便为特定于语言环境的字符腾出空间,例如ç, é, è...(请原谅我的法语)。

即使在我使用了很长时间的这些系统上,也从未使用过三元组,因为存在丑陋的实用替代方案:在法语系统上,重音字母(如éè)被键入,但 C 编译器会将其解释为{}。它使 C 编程变得晦涩难懂,并促使许多程序员改用美式 QWERTY 键盘和语言环境(或等效)。

这已成为过去,只具有历史意义,除了拼写错误、混淆和令人讨厌的面试问题之外,你永远不会看到这些在行动。

关于后者,我无法抗拒发布这个:

fnmatch即使我强制使用有效日期,我也无法验证我的日期模板,这段代码有什么问题:

#include <stdio.h>
#include <fnmatch.h>
int main() {
    char date[] = "01/01/1988";
    if (fnmatch("??/??/????", date, 0))
        printf("invalid date format\n");
    return 0;
}
于 2016-01-04T01:18:37.527 回答