可以输出自己的源程序代码(quine)
Quine 以哲学家 Willard van Orman Quine (1908-2000) 而命名,表示一个可以生成他自己的完全的源代码的程序。编写出某个语言中最简短的 quine 通常作为黑客们的消遣。
作为真正的 quine ,有一些约定:程序不能接受输入或者是打开文件,因为那样就可以直接输入源代码或者是把源代码文件直接打开再重新打印出来,就没有什么意思了;同时,一个完全空白的程序(产生完全空白的输出,即没有输出)也并不能称作 quine 。
quine 的想法最初出现在 Bratley, Paul and Jean Millo. "Computer Recreations; Self-Reproducing Automata", Software — Practice & Experience, Vol. 2 (1972). pp. 397-400 中。Bratley 在看到已知的第一个这样的程序以后对 quine 产生了兴趣。这个程序于二十世纪六十年代由爱丁堡大学的 Hamish Dewar 以 Atlas Autocode 语言写成。
其实,实现这个问题的难度在于引用和字符串。
在网上找了几个C语言的例子:
char*s="char*s=%c%s%c;main(){printf(s,34,s,34);}"; main(){printf(s,34,s,34);} |
这个是Ken Thompson写的一个程序。这段程序有一些依赖, 忽略了 #include <stdio.h>, 还假设了双引号 " 的值为 34, 和 ASCII 中的值一样。这个程序要求在一行。
可以加上换行符,得到
char*s="char*s=%c%s%c;%cmain(){printf(s,34,s,34,10,10);}%c"; main(){printf(s,34,s,34,10,10);} |
解决#include依赖的问题:
#include <stdio.h> char*s="#include <stdio.h>%cchar*s=%c%s%c;%cmain(){printf(s,10,34,s,34,10,10);}%c"; main(){printf(s,10,34,s,34,10,10);} |
上面这些的关键就是利用在字符串中加入%s,这样就可以在程序中将这个字符串作为printf常用的格式化字符串,同时将其本身作为%s处所需要的参数,这样这个自我输出程序的框架就大体上出来了。
还有一个由 James Hu 发布的改进版:
#define q(k)main(){return!puts(#k"\nq("#k")");} q(#define q(k)main(){return!puts(#k"\nq("#k")");}) |
这种方式和上面的方式是不同的,应该属于两种不同的思路。这个方式是采用一些预处理器的技巧。我们知道在预处理器中有#这个运算符,它的作用是字符串化。所以,这儿就可也将宏定义进行字符串化,从而得到源代码。
总结来看,上面的两种方式,一种是使用%s来输出字符串,一种是使用#进行字符串化。
还有一种方式,有点欺骗的意思。就是在代码中读取自己这个源代码文件本身。
我们知道在编译器中一般都有预定义宏__FILE__,所以,这样我们就可以得到文件本身了。具体的代码如下:
#include <stdio.h> char buff[80]; main() { FILE *fp;
fp = fopen(__FILE__,"r"); while( !feof(fp) ) { printf("%s",fgets(buff,79,fp)); } fclose(fp); } |
看一个C++的例子
#include <iostream.h> #define ENIUQ(TEMPLATE) cout << TEMPLATE << "(" << #TEMPLATE << ");}";
void main() {ENIUQ("#include <iostream.h>\n#define ENIUQ(TEMPLATE) cout << TEMPLATE << \"(\" << #TEMPLATE << \");}\";\n\nvoid main()\n{ENIUQ");} |
有很多其他的例子可以在这儿找到:http://www.nyx.net/~gthompso/