6

代码:

#include <stdio.h>

int main(void)
{
  ??< puts("Hello Folks!"); ??>
}

上面的程序,当使用 GCC 4.8.1 编译时-Wall-std=c11会给出以下警告:

source_file.c: In function ‘main’:
source_file.c:8:5: warning: trigraph ??< converted to { [-Wtrigraphs]
     ??< puts("Hello Folks!"); ??>
 ^
source_file.c:8:30: warning: trigraph ??> converted to } [-Wtrigraphs]

但是当我将身体更改main为:

<% puts("Hello Folks!"); %>

没有警告被抛出。

那么,为什么编译器在使用三合字母时会警告我,而在使用二合字母时却没有?

4

4 回答 4

6

因为三元组具有默默更改代码的不良影响。这意味着相同的源文件在有和没有三元组替换的情况下都是有效的,但会导致不同的代码。这在字符串文字中尤其成问题,例如"<em>What??</em>".

语言设计和语言进化应该努力避免无声的变化。让编译器警告三元组是一件好事。

将此与 digraphs 进行对比,digraphs 是不会导致无声变化的新标记。

于 2015-05-11T12:13:55.440 回答
5

这个关于预处理的 gcc 文档为警告提供了一个很好的理由(强调我的):

Trigraphs 并不流行,许多编译器错误地实现了它们。可移植代码不应依赖于被转换或忽略的三元组。使用 -Wtrigraphs GCC 会在trigraph 可能会改变程序的含义时发出警告(如果它被转换了)

并且在这个关于 Tokenization的 gcc 文档中解释了 digraphs 不像 trigraphs 没有潜在的负面影响(强调我的):

还有六个 digraphs,C++ 标准将它们称为替代标记,它们只是拼写其他标点符号的替代方式。这是解决过时系统中缺少标点符号的第二次尝试。与 trigraphs 不同,它没有负面影响

于 2015-05-11T12:11:15.020 回答
4

可能是因为它没有负面影响,不像gcc文档中所述的三元组:

标点符号是对 C 和 C++ 有意义的所有常用标点符号。ASCII 中除了三个标点字符之外的所有标点字符都是 C 标点符号。例外是“@”、“$”和“`”。此外,所有两个和三个字符的运算符都是标点符号。还有六个二合字母,C++ 标准称之为替代标记,它们只是拼写其他标点符号的替代方式。这是解决过时系统中缺少标点符号的第二次尝试。与三元组不同,它没有负面影响,但覆盖范围不广。有向图及其对应的正常标点符号是:

 Digraph:        <%  %>  <:  :>  %:  %:%:
 Punctuator:      {   }   [   ]   #    ##
于 2015-05-11T12:12:57.490 回答
3

三元组很讨厌,因为它们使用可以合法出现在有效代码中的字符序列。用于导致经典 Macintosh 代码编译器错误的常见情况:

unsigned int signature = '????';  /* Should be value 0x3F3F3F3F */

Trigraph 处理将把它变成:

unsigned int signature = '??^;  /* Should be value 0x3F3F3F3F */

这当然不会编译。在一些稍微罕见的情况下,这样的处理可能会产生可以编译的代码,但与预期的含义不同,例如

char *template = "????/1234";

这将变成

char *template = "??S4"; // ??/ becomes \, and \123 becomes S

不是预期的字符串文字,但仍然完全合法。

相比之下,有向图是相对良性的,因为除了一些可能涉及宏的奇怪极端情况之外,在没有这种处理的情况下,没有包含可处理有向图的代码具有合法意义。

于 2015-07-01T20:04:03.413 回答