为什么三字符组在现代C++编译器中会产生错误?

8

使用Turbo C++编译三字符程序的屏幕截图

即使在GCC编译器中,没有显式指定三字符属性,三字符也无法被编译。

#include<stdio.h>

int main()
 {
 int a=4;
 if((a==4) ??! (a==5))
   printf("\nHello world!");
 return 0;
 }

这个程序保存为try.c,只有在我们指定gcc -Wall -trigraphs try.c时才能在GCC编译器中编译,并且仍然显示警告。

你能列举一些编译器,它们可以处理三字符组并且不会产生任何错误或警告吗?


1
你所说的“现代”GCC版本是哪个? - Pubby
3
此外,它们默认关闭,因为它们只在脑损伤的系统上才是必需的,这些系统缺少关键字符,例如 <{。你和你的代码不太可能遇到这样的系统。 - user149341
5
三连字符从未“主导”。它们只在极少数情况下需要使用。此外,Turbo C++是一款古老的垃圾软件,不是“现代编译器”。(抱歉)GCC 4和Clang是现代编译器。 - user149341
8
@PraveenVinny Turbo C++已经有近20年的历史了。好的教育机构不使用它。它不支持命名空间、异常处理等C++所应具备的许多特性,甚至不用提C++11了。 - Masked Man
1
是的,你说得对。非常不幸的是,许多印度学校和大学实验室仍然使用Turbo C++,供学生练习C++。 - Praveen Vinny
显示剩余9条评论
4个回答

12

三字符组(trigraphs)是由1989年的ANSI C标准引入的,后来所有的C标准都保留了它们(到目前为止,即将发布的C23标准将放弃它们)。它们也出现在1998年发布的第一个ISO C++标准中,并且在后来的所有C++标准中包括C++14。(感谢Jonathan Leffler和dyp查找细节并指出C++17版本已删除三字符组)。

引用C++17标准草稿:

对原始特性的影响:使用三字符组的有效C++2014代码可能在这个国际标准中无效或者具有不同的语义。如果它们出现在原始字符串字面值之外,实现可以选择按照C++2014中指定的方式翻译三字符组,作为从物理源文件字符到基本源字符集的实现定义映射的一部分。

在任一语言中,它们都不是可选功能(在C++17之前); 所有符合标准的编译器必须支持它们,并按照相应的语言标准进行解释。

例如,如果这个程序:

#include <stdio.h>
int main(void) {
    if ('|' == '??!') {
        puts("ok");
    }
    else {
        puts("oops");
    }
    return 0;
}

如果输出了oops,那么你的编译器就不符合规范。

但是许多C编译器默认情况下并不完全符合标准。只要编译器能够以某种方式符合标准,这对于标准来说就足够了。(gcc需要使用-pedantic-std=...来实现这一点。)

即使编译器完全符合规范,标准也没有禁止编译器警告任何它喜欢的内容。符合规范的C编译器必须诊断任何语法规则或约束的违反,但它可以发出任意数量的额外警告 - 它不必区分必需的诊断和其他警告。

三字符组很少被使用。绝大多数开发系统直接支持三字符组替换的所有字符:#[\]^{|}~

事实上,三字符组被错误地使用的次数很可能比它们被正确使用的次数还要多:

fprintf(stderr, "What just happened here??!\n");

关于可能改变程序含义的三字符序列(相对于如果该语言没有三字符序列将会具有的含义)的警告,这是符合ISO标准并且在我的观点中也是非常合理的。大多数编译器可能都有选项来关闭此类警告。

相反地,对于一个不实现三字符序列的C++17编译器,警告那些在C++14或更早版本中被视为三字符序列的序列或提供支持三字符序列的选项都是合理的。同样,为禁用此类警告提供选项也是一件好事。


2
请注意,三字符序列在C++14中已被弃用,并将从C++17中删除。 - Jonathan Leffler
1
不,我没有C++14的草案说它们已经被弃用了。我知道它们被认为是弃用了,但没有直接的证据。我在谷歌上搜索了“c++14三字符”,找到了IBM在这篇论文中说,尽管还存在与EBCDIC的问题,他们将单独解决它们而不是通过标准来解决。你也可以在这篇论文中找到《移除三字符?!》。墙上的字迹很明显——但我可能会冒昧地认为C++14就是墙壁,包含了这些字迹。 - Jonathan Leffler
@dyp:感谢您提供的信息;我已经再次更新了答案。 - Keith Thompson
@JonathanLeffler:请查看更近期的评论。 - Keith Thompson
谢谢。我的理解是,三字符标识符在C++14中(这也是您现在回答的内容)。我不知道“未来方向”部分(如果存在的话;我在C++11中看不到任何内容,并且我没有官方副本C++14)是否提到了三字符标识符。C11标准有两个“未来方向”部分,一个用于语言(6.11),另一个用于库(7.31)。 - Jonathan Leffler
显示剩余3条评论

5

GCC对三字符组很敏感。您必须显式启用它们:

gcc -trigraphs ...

GCC 4.7.1手册表示:

-trigraphs

支持ISO C三字符(trigraphs)。选项-ansi(以及严格的ISO C一致性的-std选项)暗示了-trigraphs

手册还提到:

-Wtrigraphs

如果遇到可能改变程序含义的三字符(trigraphs),则发出警告(不会警告在注释中的三字符)。此警告由-Wall启用。


2
他们可能是默认关闭的。
“一些编译器支持关闭三字符识别的选项,或者默认禁用三字符并需要选项来启用它们。”
GCC可能是后者之一。虽然它应该默认忽略并发出警告,但在这种情况下忽略可能会导致编译错误。

1

三字符序列在编译的早期阶段就被转换了,甚至可以在字符串字面量中替换。这使得由三字符序列转换引起的错误非常难以检测(如果您考虑使用日志进行调试,并且在源代码中找到输出,则情况最糟)。

您看到的警告将帮助您快速发现可能的罪魁祸首,以跟踪错误的来源。基本上它是在“警告”您某些东西可能不像您想象的那样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接