Quine-输出自己源代码的程序
高中就对这东西感兴趣过,听群里有人聊过一个阿卡林形状的代码输出另一个角色图案的小代码,然后还能继续编译运行输出其他角色,有时间找找。
当时是有人在聊那个图像与自己表达式相同的函数,然后想起来这个叫quine的东西,可以输出自己的源代码(虽然还是和那个函数相比逊色很多)。
github上有个工程(上图),好像是一个代码运行之后可以生成下一个代码,然后再用下一个语言运行生成再下一个代码,然后甚至能循环回来?(目前对能循环这件事存疑)但是不循环回来就没啥意思了,没仔细看过,可能真的能循环?
最近学校有个工作室开了个什么十行代码的活动,可以提交文艺代码,我就想起来这个东西。于是想研究能不能在quine里夹带些别的东西。
网上有很多输出自己源代码的程序,也分为了几个流派(那种用文件操作的谔谔,我们不考虑那种)。
我最早接触到的且比较简洁易懂的是下面这份:
#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表示和字符串表面上一模一样,那字符串就不能出现特殊字符,比如回车'\n',因为表面是'\n',如果当做字符串输出的话,会把回车输出出来。
另一个问题是双引号,我们如果使用字符串来实现quine,我们代码必须出现双引号,但是字符串内部是不能有双引号的,如果加''来输出双引号的话''自己就输出不出来了。
为了解决回车和双引号的问题,我们统一用%c传参的形式来表示,ASCII码中10是换行符,34是双引号,我们在printf中传入这些数字,此时我们的代码就只有'%'和数字、字母组成了。
这里可以学到printf的一个新用法,如果printf括号中有多个内容,第一个是字符串,剩下的为字符串或单个字符,后面的内容会作为第一个字符串中的格式字符输出。
奇怪的是,如果我们单独把s输出出来,那些转义字符%c或%s会乱码,但是如果我们把s嵌套进%s中第二遍输出,就能顺利地把%c%s当做字面量来输出,这玄学的地方应该得问问编译器怎么写的了。
那么我想在里面塞一些自己的字符串,比如输出一个"qwq",我们发现把这三个字符塞在s中的任何位置都是不合法的。
我们可以新开一个字符串p记录"qwq",然后在s中输出z这一行双引号左边的部分和p双引号右边的部分。代码如下:
#include <stdio.h>
char s[]="#include <stdio.h>%cchar s[]=%c%s%c;%cchar z[]=%c%s%c;%cmain(){printf(s,10,34,s,34,10,34,z,34,10,10);}%c";
char p[]="qwq";
main(){printf(s,10,34,s,34,10,34,z,34,10,10);}
我们可以让z成为一个好几行的字符画,可惜每一行都要在s中记录双引号和换行,z是五行的时候s字符串就很长了,不太美观,我把它分成了两份,int main后面的部分用z字符串来存,长度折半,更美观些。
效果如下,正好十行,自定义的部分放上了2000VOLT的两句词:
#include <stdio.h>
char s[]="#include <stdio.h>%cchar s[]=%c%s%c;%cchar p[5][99]={%c%c%s%c,%c%c%s%c,%c%c%s%c,%c%c%s%c,%c%c%s%c};%cchar z[]=%c%s%c;%c%s";
char p[5][99]={
" +----------------------------------------------------------------+ ",
" | Sometimes I thought the tears would never end. | ",
" | Let the healing open the true emotion of these tears. | made by ",
" | ----[Dynamix] 2000VOLT - ALTA | maple276 ",
" +----------------------------------------------------------------+ "};
char z[]="int main(){printf(s,10,34,s,34,10,10,34,p,34,10,34,p[1],34,10,34,p[2],34,10,34,p[3],34,10,34,p[4],34,10,34,z,34,10,z);}";
int main(){printf(s,10,34,s,34,10,10,34,p,34,10,34,p[1],34,10,34,p[2],34,10,34,p[3],34,10,34,p[4],34,10,34,z,34,10,z);}