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