本节书摘来自异步社区《C专家编程》一书中的第1章,第1.11节,作者 【美】Perter Van Der Linde,更多章节内容可以访问云栖社区“异步社区”公众号查看
1.11 轻松一下——由编译器定义的Pragmas效果
*软件基金会(Free Software Foundation)是一个独特的组织,它由MIT*黑客Richard Stallman所创立。顺便提一下,我们所说的“黑客”,它的原先意思是“天才程序员”。后来这个称呼被媒体所贬损,致使它在局外人眼中成了“邪恶的天才”的代名词。和形容词“bad”一样,“黑客”现在也有两个相反的意思,必须通过上下文才能明白它的确切意思。
Stallman成立*软件基金会的初衷是:软件应该是免费的,所有人都可以*使用。FSF的宗旨是“消除在计算机程序拷贝、重发布、理解和修改方面的限制”,它雄心勃勃地想建立一个UNIX的*软件实现方案,称为GNU(它代表“GNU's Not UNIX”,对,确实如此)。
许多计算机科学研究生和其他人赞同GNU的哲学,他们设计软件产品,由FSF进行打包并免费发布。通过这些甘心奉献的有天赋的程序员们的辛勤劳动,产生了一些优秀的软件作品。FSF最好的作品之一就是GNU C编译器系列。gcc是一个健壮的、在代码优化方面具有创造性的编译器,可以在很多硬件平台使用,有时甚至比编译器厂商的产品更为优秀。gcc并不适合所有的项目,它在维护性和未来版本连续性方面仍存在一些问题。在现实的开发中,除了编译器之外,还需要很多工具。曾有很长一段时间,GNU的调试器无法在共享库中工作。而且在开发时,GNU C偶尔会让人感到眼花缭乱。
在制订ANSI C标准时,引入了pragma指示符,这个指示符来源于Ada。#pragma用于向编译器提示一些信息,诸如希望把某个特定函数扩展为内联函数,或者取消边界的检查。由于它并非C语言所固有,pragma遭到了一个gcc编译器设计者的积极抵制,他把这个“由编译器定义的”的效果做得很搞笑——在gcc 1.34版,如果使用了pragma,将会导致编译器停止编译,而是运行一个计算机游戏!在gcc手册中有如下说明:
在ANSI C标准中,“#pragma”指令会产生一个由编译器定义的任意效果。在GNU C预处理器中,一旦遇见“#pragma”指令,它首先试图运行“rogue”游戏;如果失败,尝试运行“hack”游戏;如果还是失败,它会尝试运行GNU Emacs,显示汉诺塔(Tower of Hanoi)。如果仍然失败,它就报告一个致命错误。总之,预处理过程不会继续下去。
—— GNU C编译器1.34版手册
GNU C编译器中关于预处理器的那部分源代码如下:
/ *
* #pragma指示符的行为是由编译器定义的。
* 在GNU C编译器中,它的定义如下:
* /
do_pragma()
{
close(0);
if(open("/dev/tty", O_RDONLY, 0666) != 0)
goto nope;
close(1);
if(open("/dev/tty", O_WRONLY, 0666) != 1)
goto nope;
exel("/usr/games/hack", "#pragma", 0);
exel("/usr/games/rogue", "#pragma", 0);
exel("/usr/new/emacs", "-f", "hanoi", "9", "-kill", 0);
exel("/usr/local/emacs", "-f", "hanoi", "9", "-kill", 0);
nope:
fatal("you are in a maze of twisty compiler features, all different");
}
特别好笑的是,用户手册中的描述是错误的,它把“hack”和“rogue”的次序搞反了。
[1] 学习、使用和实现PL/I的困难使一位程序员写了这样一首打油诗:“IBM有个PL/I,语法比JOSS还糟糕,到处都见它踪影,实实在在是垃圾。JOSS是个老古董,它可不是因简单而闻名。”
[2] “BCPL:A Tool for Compiler Writing and System Programming(BCPL,编译器编写和系统编程的工具),” Martin Richards, Proc. AFIPS Spring Joint Computer Conference, 34(1969), pp.557-566。BCPL并非“Before C Programming Language(C前身编程语言)”的首字母缩写,尽管这是个有趣的巧合。它的确切意思是“Basic Combined Programming Language(基本组合编程语言)”。basic的意思是“不花哨”,它是由英国伦敦大学和剑桥大学的研究人员合作开发的。Multics实现了一种BCPL编译器。
[3] 本书原版出于1994年,当时距1970年还不到30年。——译者注
[4] ANSI C Rationale(单独)可通过匿名FTP,从ftp.uu.net下载,位于/doc/standards/ansi/X3.159-1989/(如果你不明白匿名FTP,赶紧到附近的书店买一本关于Internet的书,免得成为信息高速公路上的“跛行的羔羊”)。Rationale的纸版书也已出版,ANSI C Rationale, 新泽西Silicon Press,1990。ANSI C标准本身无法从任何ftp站点下载,因为标准印刷本的营业收入是ANSI的重要收入来源之一。
[5] 如果你想刨根问底,它位于第5.1.1.3段,“Diagnostics(诊断)”。作为一个语言标准,它不会简单地说“在一个不正确的程序里,你必须为每个错误准备一个标志”。作为标准,其用辞必然骈四骊六,仿佛是由靠玩弄文字吃饭的律师所撰写的。它的正式用辞如下:“一个遵循标准的实现应该*至少为每个翻译单元产生一条诊断信息,其中包含了所有违反语法规则或约束的行为。在其他情况下不必产生诊断信息”。
*Brian Scearce+ 所总结的有用规律——如果你听到一个程序员说“应该(shall)”,那么他一定在引用标准里的说法。
+嵌套脚注(nested footnote)的发明者。
[6] The New Hacker's Dictionary把语言律师定义为“能从200多页的手册中提取5句话,拼起来放到你面前,你只要一看就能明白自己问题的答案的人”,嘿!在这个例子的情况下正是如此。
[7] 即int是32位。——译者注
[8] 即long 是32位而int是16位。——译者注
[9] 即long和int均为32位。——译者注
[10] The Elements of Programming Style, Kernighan(对,就是那个Kernighan)和Plauger,纽约,McGraw Hill,1978。这是一本文字流畅、细节真实的优秀作品——非常值得购买,你能从中获益良多。