如何编写一个可以输出自己源代码的程序?

如何编写一个可以输出自己源代码的程序,要满足以下要求:
1.该程序不可以从外部读取输入。
2.该程序的输出不可以为空。
想了很久始终无法想明白要如何做到。。。

阅读 12k
11 个回答

这个叫做Quine
https://en.wikipedia.org/wiki/Quine_(computing)
https://zh.wikipedia.org/wiki/%E8%87%AA%E7%94%A2%E7%94%9F%E7%A8%8B%E5%BC%8F
Java 版本比较罗嗦,可以去wiki看看其他语言的版本

public class Quine
{
  public static void main(String[] args)
  {
    char q = 34;      // Quotation mark character
    String[] l = {    // Array of source code
    "public class Quine",
    "{",
    "  public static void main(String[] args)",
    "  {",
    "    char q = 34;      // Quotation mark character",
    "    String[] l = {    // Array of source code",
    "    ",
    "    };",
    "    for(int i = 0; i < 6; i++)           // Print opening code",
    "        System.out.println(l[i]);",
    "    for(int i = 0; i < l.length; i++)    // Print string array",
    "        System.out.println(l[6] + q + l[i] + q + ',');",
    "    for(int i = 7; i < l.length; i++)    // Print this code",
    "        System.out.println(l[i]);",
    "  }",
    "}",
    };
    for(int i = 0; i < 6; i++)           // Print opening code
        System.out.println(l[i]);
    for(int i = 0; i < l.length; i++)    // Print string array
        System.out.println(l[6] + q + l[i] + q + ',');
    for(int i = 7; i < l.length; i++)    // Print this code
        System.out.println(l[i]);
  }
}

除非源码文件还在,不然二进制文件无法将本身再解释成源代码,解释其他二进制也不行。目前,很多编译器在解析源代码时候都会进行优化,对应到二进制的一条条指令。这些指令没办法再翻译成高级语言了。就像2+2=4,但4不一定2+2.

C语言的话,用宏定义应该可以构造特殊的程序,使其输出恰好等于其源码,但是如果你想要输出全部源代码(包括include和注释)就做不到了。要想从编译好的可执行文件中获得代码就属于逆向工程了,而且有些编译过程本身就是不可逆的。

最简单的C:

define EXEC_OUTPUT(expr) printf("%s", #expr); expr;

EXEC_OUTPUT(a = 1);

不过只支持简单表达式,不支持本身有字符串的表达式,不支持宏,不支持全局变量。。。

((lambda (x)

   (list x (list (quote quote) x)))
  (quote
     (lambda (x)
       (list x (list (quote quote) x)))))

编译后的二进制程序显然不行。脚本程序依赖解释器,也不符合要求。
所以这是一个鸡生蛋蛋生鸡的问题?

php

简洁版:

echo file_get_content(__FILE__);

酷炫版:

highlight_file(__FILE__);
新手上路,请多包涵

在编译期将读取源代码,以字符串形式存放到程序中
程序运行时根据不同参数输入
这么个思路

首先,为了限制篇幅,我在外部定义一个quote函数,作用是将一个字符串中的特殊字符转义起来。这个函数就不纳入源文件了,否则代码太长:

// quote.h

char* quote(const char* s)
{
    static char buf[512];
    
    for(unsigned len = 0; *s; s++) {
        if(*s == '\n') { buf[len++] = '\\'; buf[len++] = 'n'; }
        else if(*s == '\"') { buf[len++] = '\\'; buf[len++] = '"'; }
        else buf[len++] = *s;
    }

    return buf;
}

这个源文件是这样的:

#include <stdio.h>
#include <quote.h>

int main()
{
    const char *s = "#include <stdio.h>\n#include <quote.h>\n\nint main()\n{\n    const char *s = \"%s\";\n    printf(s, quote(s));\n}";
    printf(s, quote(s));
}

s就是一个把整个源文件压扁成字符串,然后在字符串里代表自身的地方用%s代替。总的来说只是算printf的一种有趣的应用罢了。

这个有个缺陷,就是在你改动源码的时候,你要相应地改动s。你可以将quote函数也纳入源码,但是要相应地在加入单引号和斜杠的转移。

又没说不能用编译指令……

x.c:

X

Makefile:

x: x.c
    gcc 2.c -DX="main(){puts(\"X\");}"
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题