2021年写的第一个程序:Quine问题—可以输出自己的源代码


2021年的元旦节,放假在家,闲来无事,从书架上翻到一本《枕边算法书》。翻开几页就看到了Quine问题:如何写一个可以打印出自己的源代码。忍住好奇没去看解答,想看看这类谜题自己还能不能解出来。

第一次尝试

乍看觉得好像还比较简单的样子,于是直接开写代码:

#include <stdio.h>
int main(){
 printf("#include <stdio.h>\nint main(){\n printf(\"#include ... \");");
}

写着写着发现不对啊,这样写就好像两个镜子对着照一样,无限的引用啊…
恼火得很。

换个思路

绞尽脑汁花了好长时间,想到这个核心思路:

char* s="char* s=%s;printf(s,s);"printf(s,s);

利用printf函数的%s转义符,用格式化字符串自身替换掉%s所在的位置。
感觉这个思路应该没问题,但实际调试的时候卡在引号“、换行符等转义符的处理上。在定义字串s的时候是不可避免会遇到\n “这样的情况,这些转义符在被%s替换后是应该直接打印出”\n"的字符,而不是换行,为了解决这个问题,我很傻的写了下面这个很蠢的代码。

丑陋的实现

自己写了个Print函数,这个函数实现的功能是打印字符串,如果字符串中有@符号,则把@符号替换为整个字符串自身。

#include <stdio.h>
void Print(char* s){
//代码实现太丑,就不贴出来了
}
int main(){
	Print("#include ...太长省略...int main(){\n Print(@);\n}");
}

代码工作是OK的,完全可以输出与自身一摸一样的源代码。但是看着这长达100多行的代码,我的内心是崩溃的。

借鉴优化

一定有更好的办法的,抱着这个想法,我开始继续尝试。最终找到了下面的方法:

#include <stdio.h>
int main(){
    char* s = "#include <stdio.h>%cint main(){%c    char* s = %c%s%c;%c    printf(s,10,10,34,s,34,10,10);%c}";
    printf(s,10,10,34,s,34,10,10);
}

核心思路其实没有变,只是避免了在定义字串的时候使用\n "等转义符,而是直接使用ASCII 码值来处理。

Apple的思路

在我尝试过程中,Apple曾经给过一个取巧的思路:直接读源代码文件,把文件内容输出就行了。我很诧异我怎么没有就没想到过这个思路呢。这里给个vbs的实现。

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile(WScript.ScriptFullName, 1)
WScript.Echo objtextfile.ReadAll

其他的思路

发现这个经典问题有太多高手的实现了,有些真的让人感叹,这是什么脑子,怎么想的出来。比如:

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

很晕吧。

网上搜一搜还有很多很好的思路。看了一圈就发现一个大问题:我为什么想不出来呢?哼哼!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值