0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

C言語でQuineを書いてみました。

Quineとは、簡単にいうと「自分自身のソースコードを出力するプログラム」です。

つまり、例えば main.c を実行すると、出力結果が main.c の中身と完全に一致するようなプログラムです。

最初に聞くと「それって cat main.c すればいいのでは?」と思うかもしれませんが、Quineでは基本的に外部ファイルを読み込まずに、自分自身の内容を出力する必要があります。

Quineの条件

Quineには、だいたい以下のような条件があります。

  • 自分自身のソースコードを出力する
  • ソースファイルを直接読み込まない
  • 出力結果が元のソースコードと完全に一致する

今回はC言語で、なるべくシンプルなQuineを書いてみます。

完成コード

#include <stdio.h>

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

実行してみる

例えば、上のコードを main.c として保存します。

gcc main.c -o quine
./quine

実行すると、自分自身のソースコードが出力されます。

さらに確認するなら、出力結果を別ファイルに保存して diff してみます。

./quine > output.c
diff main.c output.c

何も表示されなければ、main.coutput.c は一致しています。

仕組み

ポイントはこの部分です。

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

Quineでは、自分自身のコードを文字列として持ち、その文字列を printf で出力します。

ただし、単純にソースコード全体を文字列に入れようとすると問題があります。

例えば、C言語で文字列を表すには " が必要です。

char *s = "hello";

しかし、ソースコード自身を文字列として扱う場合、その中にある " も出力しなければいけません。

そこで、" を直接文字列に書く代わりに、ASCIIコードを使っています。

34

ASCIIコードの 34" を表します。

また、改行には 10 を使っています。

10

ASCIIコードの 10 は改行を表します。

つまり、この部分では、

printf(s, 10, 10, 10, 10, 34, s, 34, 10, 10, 10);

%c に改行やダブルクォートを埋め込み、%s に文字列 s 自身を埋め込むことで、自分自身のコードを再構築しています。

フォーマット文字列を見る

文字列 s の中には、出力したいソースコードの雛形が入っています。

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

この中で重要なのは以下です。

  • %c : 文字を1文字出力する
  • %s : 文字列を出力する

%c には改行や " を渡します。

%s には、文字列 s 自身を渡します。

printf(s, ..., s, ...);

ここがQuineっぽいところです。

自分自身を表す文字列 s を、出力の途中にもう一度埋め込んでいます。

なぜ s を2回使うのか

Quineでは、プログラム本体を出力するだけでなく、プログラム中にある「プログラム本体を表す文字列」も出力する必要があります。

つまり、以下の2つを同時に満たす必要があります。

  1. ソースコード全体を出力する
  2. そのソースコードの中にある文字列 s の中身も正しく出力する

このため、文字列 s は、

  • 出力のテンプレートとして使われる
  • ソースコード内の文字列リテラルとしても出力される

という2つの役割を持っています。

ハマりやすいポイント

ダブルクォートの扱い

一番ややこしいのは " の扱いです。

C言語では文字列を " で囲むため、ソースコードとして " を出力したい場合は工夫が必要です。

今回は 34 を使って出力しています。

printf("%c", 34);

これは以下と同じ意味です。

printf("\"");

改行の扱い

改行も直接文字列に書くと見通しが悪くなるため、今回は 10 を使っています。

printf("%c", 10);

これは \n と同じように改行を出力します。

出力結果の完全一致

Quineでは、空白や改行の数も含めて一致している必要があります。

そのため、インデントや末尾の改行がずれていると、diff で差分が出ます。

見た目では同じに見えても、実際には改行やスペースが違うことがあります。

まとめ

C言語でQuineを書いてみました。

特に難しいのは、自分自身を文字列として持ちながら、その文字列自身も正しく出力するところです。

今回のポイントは以下です。

  • ソースコードの雛形を文字列 s として持つ
  • printfs を出力する
  • %ss 自身を渡す
  • %c を使って改行や " を出力する
  • 出力結果がソースコードと完全一致するように調整する

最初は少し不思議な感じがしますが、仕組みがわかると「なるほど」となる面白いプログラムでした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?