ループまたは条件なしで1から1000まで出力 のC
コードを見つけました:しかし、それがどのように機能するか理解できません。誰でもコードを調べて各行を説明できますか?
#include <stdio.h>
#include <stdlib.h>
void main(int j) {
printf("%d\n", j);
(&main + (&exit - &main)*(j/1000))(j+1);
}
_j<1000
_の場合、_j/1000
_はゼロ(整数除算)です。そう:
_(&main + (&exit - &main)*(j/1000))(j+1);
_
以下と同等です:
_(&main + (&exit - &main)*0)(j+1);
_
どちらですか:
_(&main)(j+1);
_
_j+1
_でmain
を呼び出します。
_j == 1000
_の場合、同じ行が次のように出力されます。
_(&main + (&exit - &main)*1)(j+1);
_
煮詰める
_(&exit)(j+1);
_
これはexit(j+1)
であり、プログラムを終了します。
_(&exit)(j+1)
_とexit(j+1)
は本質的に同じものです-C99§6.3.2.1/ 4を引用:
関数指定子は、関数タイプを持つ式です。 sizeof演算子または単項演算子のオペランドである場合を除き、タイプ "function Return type"の関数指定子型「型を返す関数へのポインタ」を持つ式に変換されます。
exit
は関数指定子です。単項_&
_アドレス演算子がなくても、関数へのポインターとして扱われます。 (_&
_はそれを明示的にします。)
また、関数呼び出しは§6.5.2.2/ 1以降で説明されています。
呼び出された関数を示す式のタイプは、関数へのポインタ voidを返すか、配列型以外のオブジェクト型を返す必要があります。
したがって、exit(j+1)
は、関数型から関数へのポインター型への自動変換により機能し、_(&exit)(j+1)
_は、関数へのポインター型への明示的な変換でも同様に機能します。
そうは言っても、上記のコードは準拠していません(main
は2つの引数を取るか、まったく引数を取りません)、_&exit - &main
_は§6.5.6/ 9に従って未定義です:
2つのポインターが減算されると、-両方が同じ配列オブジェクトの要素を指す、または配列オブジェクトの最後の要素の1つ後; ...
_(&main + ...)
_の追加はそれ自体で有効であり、使用することができます。if§6.5.6/ 7以降、追加された数量はゼロでした言う:
これらの演算子の目的上、配列の要素ではないオブジェクトへのポインタは、要素の型としてオブジェクトの型を持つ長さ1の配列の最初の要素へのポインタと同じように動作します。
したがって、_&main
_にゼロを追加しても問題ありません(ただし、あまり使用されません)。
再帰、ポインター演算を使用し、整数除算の丸め動作を利用します。
j/1000
項は、すべてのj < 1000
について0に切り捨てられます。 j
が1000に達すると、1と評価されます。
a + (b - a) * n
があり、n
が0または1の場合、n == 0
の場合はa
、n == 1
の場合はb
になります。 a
およびb
に対して&main
(main()
のアドレス)および&exit
を使用すると、j
が1000未満の場合は(&main + (&exit - &main) * (j/1000))
という用語は&main
を返し、そうでない場合は&exit
を返します。結果の関数ポインタには、引数j+1
が渡されます。
このコンストラクト全体が再帰的な動作になります。j
が1000未満の場合、main
は自身を再帰的に呼び出します。 j
が1000に達すると、代わりにexit
を呼び出して、プログラムを終了コード1001で終了させます(これは一種のダーティーですが動作します)。