第一个程序是在 C 中实现 quine 的示例。在较高级别上,它定义了一个宏,该宏q()
创建了一个main()
打印出两行的定义。第一行是参数本身,第二行是包装在对q()
自身的调用中的参数。所以,下面的程序:
#define q(k)int puts();int main(){puts(#k"\nq("#k")");}
q(foo)
扩展为:
int puts();int main(){puts("foo""\nq(""foo"")");}
编译并运行时,它会产生以下输出:
foo
q(foo)
将宏定义本身替换为foo
quine。宏并没有真正调用自己,它是在定义它的相同文本上调用的。在 C 中,宏不会递归扩展(C.99 §6.10.3.4 ¶2)。
如问题中所述,该程序在 GCC 下使用严格的 C.99 设置 ( ) 毫无怨言地编译-pedantic -std=c99
。该程序仅使用标准 C 功能,并符合 C.99 和 C.11。
- 宏替换 (C.99 §6.10.3),带有参数替换 (C.99 §6.10.3.1) 和
#
“字符串化”运算符 (C.99 §6.10.3.2)。
- 具有未指定参数列表的函数声明(C.99 §6.7.5.3 ¶14)。
- 字符串文字连接(C.99 §5.1.1.2 ¶1)。
- 默认
main()
返回值(C.99 §5.1.2.2.3 ¶1)。
特别值得注意的是,该程序不依赖字符的 ASCII 编码。
该程序将在 C.89-90 编译器上编译,但未main()
为 C.89-90 定义不返回值的行为。通过return 0;
在调用puts()
.
至于第二个节目,也是一个quine。但是,它不符合 C.89-90、C.99 和 C.11。这是因为它依赖于puts()
为逻辑非运算符返回一个正数,因此返回值为0
. 但是,C 只要求puts()
在成功时返回一个非负值(C.99 §7.19.7.10 ¶3)。只有 C.89-90 允许隐式函数声明(C.89,§3.3.2.2)。return!
通过删除,然后在调用后添加,return 0;
可以修改程序以符合 C.89-90 puts()
。
这些程序的结构在很大程度上受到了“虚构”语言 BlooP 中 quine 程序的实现的启发,该程序在Douglas R. Hofstadter所著的Gödel, Escher, Bach: An Eternal Golden Braid一书中提出(因创造了该术语而受到赞誉)奎因)。
DEFINE PROCEDURE "ENIUQ" [TEMPLATE]: PRINT [TEMPLATE, LEFT-BRACKET,
QUOTE-MARK, TEMPLATE, QUOTE-MARK, RIGHT-BRACKET, PERIOD].
ENIUQ
['DEFINE PROCEDURE "ENIUQ" [TEMPLATE]: PRINT [TEMPLATE, LEFT-BRACKET,
QUOTE-MARK, TEMPLATE, QUOTE-MARK, RIGHT-BRACKET, PERIOD].
ENIUQ'].
顺便说一句,这是一个以相反顺序打印出其源代码的程序版本:
#define q(k)r(char*s){if(*s)r(s+1);putchar(*s);}main(){r(#k"\nq("#k")\n");}
q(#define q(k)r(char*s){if(*s)r(s+1);putchar(*s);}main(){r(#k"\nq("#k")\n");})