ループや条件なしで1から1000までCを出力するコードを見つけました。しかし、それがどのように機能するのかわかりません。誰かがコードを調べて各行を説明できますか?
#include <stdio.h>
#include <stdlib.h>
void main(int j) {
printf("%d\n", j);
(&main + (&exit - &main)*(j/1000))(j+1);
}
ループや条件なしで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);
どの呼び出すmainとj+1。
の場合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演算子または単項&演算子のオペランドである場合を除いて、型が「関数の戻り型」の関数指定子は、型が「関数の戻り型へのポインタ」の式に変換されます。
exit関数指定子です。単項&アドレス演算子がない場合でも、関数へのポインタとして扱われます。(&それを明示的にするだけです。)
また、関数呼び出しについては、§6.5.2.2/ 1以降で説明しています。
呼び出された関数を表す式には、voidを返すか、配列型以外のオブジェクト型を返す関数への型ポインターが必要です。
したがってexit(j+1)、関数型から関数へのポインター型への自動変換により機能し、関数へのポインター型へ(&exit)(j+1)の明示的な変換でも機能します。
とはいえ、上記のコードは準拠しておらず(main2つの引数を取るか、まったく引数を取らない)、&exit - &main§6.5.6/ 9に従って未定義であると私は考えています。
2つのポインターが差し引かれると、両方とも同じ配列オブジェクトの要素を指すか、配列オブジェクトの最後の要素の1 つ後を指します。...
追加(&main + ...)はそれ自体有効であり、§6.5.6/ 7で次のように追加された数量がゼロの場合に使用できます。
これらの演算子の目的では、配列の要素ではないオブジェクトへのポインターは、オブジェクトの型を要素型として、長さが1の配列の最初の要素へのポインターと同じように動作します。
したがって、ゼロを追加しても&main問題ありません(ただし、あまり使用しません)。
fooは、はポインターで&fooあり、そのポインターのアドレスです。2番目の場合、fooは配列で、&foofooと同等です。
((void(*[])()){main, exit})[j / 1000](j + 1);
&foofoo配列に関しては同じではありません。&fooは配列fooへのポインタ、は最初の要素へのポインタです。彼らは同じ価値を持っています。関数の場合fun、&fun両方とも関数へのポインターです。
再帰、ポインター演算を使用し、整数除算の丸め動作を利用します。
j/1000用語はすべてのために0に切り捨てj < 1000。一度jに達する1000年、それは1と評価されます。
ここで、がある場合a + (b - a) * n、nは0または1のどちらかで、if とaif n == 0になりbますn == 1。&main(のアドレスmain())と&exitfor aおよびを使用するとb、が1000未満の場合に用語(&main + (&exit - &main) * (j/1000))が返されます。次に、結果の関数ポインタに引数が渡されます。&mainj&exitj+1
このコンストラクト全体は、再帰的な動作になりjます。1000未満の場合、mainそれ自体を再帰的に呼び出します。j1000 に達すると、exit代わりに呼び出し、プログラムを終了コード1001で終了させます(これはダーティですが、機能します)。
exitは、終了コードを引数として取り、現在のプロセスを終了します。その時点で、jは1000なので、j + 1は1001に等しく、これが終了コードになります。
mainC ++では呼び出すことができません。