3

次のプログラムは、自分自身を呼び出す C マクロのように見えます。

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

コンパイルして正常に動作します。それは自分自身を印刷します。

このコードは本当に C ですか? つまり、正しく動作するために標準 C 以外のものに依存していますか?

@devnull は、この質問に同様のプログラムがあることを指摘しました。

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

このプログラムは、正しく動作するために標準 C 以外のものに依存していますか?

4

1 に答える 1

8

最初のプログラムは、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");})
于 2013-08-01T00:02:07.000 に答える