3

下面的程序看起来像一个调用自身的 C 宏。

#define q(k)int puts();int main(){puts(#k"\nq("#k")");}
q(#define q(k)int puts();int main(){puts(#k"\nq("#k")");})

它编译并运行良好。它自己打印出来。

这段代码真的是 C 吗?也就是说,它是否依赖标准 C 之外的任何东西才能正常工作?

@devnull 指出这个问题有一个类似的程序:

#define q(k)main(){return!puts(#k"\nq("#k")");}
q(#define q(k)main(){return!puts(#k"\nq("#k")");})

该程序是否依赖标准 C 之外的任何内容才能正常工作?

4

1 回答 1

8

第一个程序是在 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)

将宏定义本身替换为fooquine。宏并没有真正调用自己,它是在定义它的相同文本上调用的。在 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");})
于 2013-08-01T00:02:07.000 回答