最初のプログラムは、C での quine の実装の例です。高レベルでは、2 行を出力q()
する定義を作成するマクロを定義します。main()
最初の行はそれ自体の引数で、2 行目はq()
それ自体への呼び出しにラップされた引数です。したがって、次のプログラム:
#define q(k)int puts();int main(){puts(#k"\nq("#k")");}
q(foo)
次のように展開します。
int puts();int main(){puts("foo""\nq(""foo"")");}
コンパイルして実行すると、次の出力が得られます。
foo
q(foo)
の代わりにマクロ定義自体を置き換えるfoo
と、クワインになります。マクロは実際にはそれ自体を呼び出すのではなく、それを定義する同じテキストで呼び出されます。C では、マクロは再帰的に展開されません (C.99 §6.10.3.4 ¶2)。
質問で述べたように、プログラムは厳密な C.99 設定を使用して GCC の下で問題なくコンパイルされます ( -pedantic -std=c99
)。このプログラムは、標準の C 機能のみを使用し、C.99 と C.11 の両方に準拠しています。
- マクロ置換 (C.99 §6.10.3)、引数置換 (C.99 §6.10.3.1) および
#
「文字列化」演算子 (C.99 §6.10.3.2) を使用。
- 引数リストが指定されていない関数宣言 (C.99 §6.7.5.3 ¶14)。
- 文字列リテラルの連結 (C.99 §5.1.1.2 ¶1)。
- デフォルト
main()
の戻り値 (C.99 §5.1.2.2.3 ¶1)。
特に注意すべき点として、このプログラムは文字の ASCII エンコードに依存していません。
プログラムは C.89-90 コンパイラでコンパイルされますが、値を返さない動作はmain()
C.89-90 では定義されていません。return 0;
プログラムは、呼び出しの後にを追加することで、C.89-90 に準拠するように簡単に変更できますputs()
。
2番目のプログラムに関しては、それもクワインです。ただし、C.89-90、C.99、または C.11 準拠ではありません。puts()
これは、論理否定演算子の正の数を返すことに依存しているため、戻り値は0
です。ただし、C では、puts()
成功時に負でない値のみを返す必要があります (C.99 §7.19.7.10 ¶3)。C.89-90 のみが暗黙の関数宣言を許可します (C.89、§3.3.2.2)。C.89-90 に準拠するようにプログラムを変更するには、 を削除し、呼び出しの後にreturn!
追加します。return 0;
puts()
これらのプログラムの構造は、「架空の」言語 BlooP でのクワイン プログラムの実装に大きく影響を受けています。クワイン)。
DEFINE PROCEDURE "ENIUQ" [TEMPLATE]: PRINT [TEMPLATE, LEFT-BRACKET,
QUOTE-MARK, TEMPLATE, QUOTE-MARK, RIGHT-BRACKET, PERIOD].
ENIUQ
['DEFINE PROCEDURE "ENIUQ" [TEMPLATE]: PRINT [TEMPLATE, LEFT-BRACKET,
QUOTE-MARK, TEMPLATE, QUOTE-MARK, RIGHT-BRACKET, PERIOD].
ENIUQ'].
余談ですが、ソース コードを逆順に出力するバージョンのプログラムを次に示します。
#define q(k)r(char*s){if(*s)r(s+1);putchar(*s);}main(){r(#k"\nq("#k")\n");}
q(#define q(k)r(char*s){if(*s)r(s+1);putchar(*s);}main(){r(#k"\nq("#k")\n");})