我正在学习有向图和三字母,这是我无法理解的代码。 (是的,我承认它非常难看。)
此代码可以编译:
#define _(s) s%:%:s
main(_(_))
<%
__;
%>t
这段代码也可以编译:
#define _(s) s??=??=s
main(_(_))
<%
__;
%>
但是,以下两段代码都不能编译:
#define _(s) s%:??=s
main(_(_))
<%
__;
%>
和
#define _(s) s??=%:s
main(_(_))
<%
__;
%>
这确实让我感到困惑:由于前两段代码可以编译,我想digraph和trigraph的扩展都发生在宏扩展之前。那么为什么在将digraph和trigraph一起使用时它们能够编译呢?
答案 0 :(得分:2)
有向图和三角形是完全不同的。在翻译的第1阶段,[参见注释1],在源代码被分成标记之前,Trigraph被替换。 Digraph是令牌,它们是其他令牌的替代拼写,所以直到源被分成令牌之后它们才有意义。 (“有向图”这个词不是很准确;因为它类似于“三字母”而使用它,但是有向图集包括%:%:
,它由四个字符组成。)
在完成任何令牌分析之前,??=
将替换为#
。但%:
只是一个标记,其含义与#
相同。
此外,%:%:
是与##
具有相同含义的标记。但是%:#
是两个令牌(%:
和#
),这是不合法的,因为stringify运算符(拼写为%:
或#
)只能被跟踪通过宏参数。 [见注2]如果#
是三角形替换的结果,它就不会变得非法。
如the hilarious snippet in chqrlie's answer所示,有向图和三字符之间的一个重要区别是三字符也可用于字符串。 Digraphs允许你编写C代码,即使你的键盘没有括号和octothorpi,但它们无法帮助你打印出这些字符。
§5.1.1.2,翻译阶段,第1段:
翻译语法规则的优先级由以下阶段指定。
- 物理源文件多字节字符以实现定义的方式映射到源字符集(如果需要,引入行尾指示符的换行符)。 Trigraph序列由相应的单字符内部表示替换。
醇>
§6.10.3.2, #erler ,第1段:
类似函数宏的替换列表中的每个#preprocessing token都应该是 后跟一个参数作为替换列表中的下一个预处理标记。
答案 1 :(得分:2)
对于学术方面,请看看rici记录良好的答案。
对于常识方面,除非你已经非常精通C,否则有向图和三字母完全没用,你甚至不应该浪费任何时间来讨论这个问题。它们被发明为一种支持非美国7位字符集的方法,这些字符集在20世纪80年代仍在大型机和一些小型计算机上使用。这些字符集缺少C语言所需的一些标点符号,例如#
,{
,}
等。
即使在我使用过一段时间的这些系统上,也从未使用过三角形,因为存在丑陋的实用替代方案:在法语系统上,输入了é
和è
等重音字母但是由C编译器解释为{
和}
。它使C编程模糊不清,并促使许多程序员切换到美国QWERTY键盘和Locale(或等效的)。
这是过去的事情,只有历史感兴趣,除了错别字,混淆和令人讨厌的面试问题,你永远不会看到这些在行动中。
关于后者,我无法抗拒发布这个:
即使我强制使用有效日期,我也无法让
fnmatch
验证我的日期模板,这段代码有什么问题:#include <stdio.h> #include <fnmatch.h> int main() { char date[16] = "01/01/1988"; if (fnmatch("??/??/????", datebuf, 0)) printf("invalid date format\n"); return 0; }