鍋あり谷あり

テーマを決めずに適当に書いています。

後悔はしなくても補足はする

はてなブックマークで、鍋あり谷ありとしては最多の、5users となった。
びっくり。

補足をしておくと:

main=0

こういうソースを見ると、ああBCPLの正当なる後継者なんだなと思う。私自身はBCPLの経験はないんだが。
で。
main というシンボルの指す値へジャンプ、というのが main の本質なので、ここにゼロを書いておけばゼロにジャンプする。関数を書けば関数の先頭にジャンプ。配列を書けば、配列の先頭にジャンプ。
ゼロ番地にジャンプした後死ぬかどうかは処理系依存

main(){main();}

スタックオーバーフローで死ぬ予定だったんだが、手元ではクラッシュ報告が出ずに一瞬でプログラムが終了した。何故だろう(調べてない)。

main(){*(int*)0=0;}

main(){++0[(int*)0];}

ゼロ番地への書き込みで死ぬかどうかは処理系依存。私の記憶が確かなら、MS-DOS では死なない。
とはいえ、プロセスごとにメモリ空間が別になっているようなOSでは、ゼロ番地をアクセス可能にしないのが通例なので、winNT や MacOS X なら死ぬはず。だと思う。

main(){(main-999)();}

文法違反だと思う。関数へのポインタは加算・減算できないような気がする。gcc では通るので、私の理解が間違っているのかも。
死ぬかどうかは処理系依存。同じ処理系でも運次第で死ななかったり。

main(){++*((int*)main);}

実行コードが書かれている場所が書き込み可能かどうかで死んだり死ななかったり。
手元ではどうも read only になっているようで、書いたらすぐに死んだ。

main(){memset(0,0,-1);}

これが最強。
memset(開始アドレス,値,長さ)。長さは符号なしなので、-1を入れると、32bitの処理系なら 0xffffffffU、ほぼ4Gbyteになる。
つまり、アドレス0からアドレスfffffffeまでをゼロで埋めるというコードなので、ゼロ番地への書き込みでは死なない MS-DOS のような環境でも死ぬ*1。キャッシュとか特権とかの関係でたまたま memset が最後までできてしまったとしても、0xffffffff番地の1byteが残るのみ。

main(){((int(*)())0)();}

ゼロ番地へのジャンプを、C言語で普通に書くとこうなると思う。返り値を省略したかったんだが、どうも省略できないらしい。残念。void にしなかったのは、int の方が 1 文字少ないから。
BCPL なら、int(*)()へのキャストが不要なので、0();だと思う。
あと。コメント記法に相当する文字列が含まれるので、エスケープしなくちゃいけないのが面倒だった。

main(){main(*(int*)main=0);}

何の気なしに書いてみたが、意外と難しかった。2行に分けると:
*(int*)main = 0;
main(0);
となる。
手元では1行目で死ぬが、1行目で死ななかったとすると、2行目で関数呼び出し。
ジャンプ先は、引数を先に評価する処理系なら、main の値がゼロになっているのでゼロ番地。
引数の評価が後になったとしても、本来の main 関数に再入し、そこで再び main(0); にまみえるので、今度こそゼロ番地へジャンプ。
というわけで、いずれにせよゼロ番地へ行く。とはいえ、ゼロ番地へのジャンプで死ぬかどうかは処理系依存


#写真と本文は関係ありません。

*1:コードセグメントとデータセグメントが別になっていて、ポインタが 16bit だと、死なないかも。