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/
