このCプログラムはどのように機能しますか?
main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}
そのままコンパイルします(gcc 4.6.3
でテスト済み)。コンパイル時の時刻を表示します。私のシステムでは:
!! !!!!!! !! !!!!!! !! !!!!!!
!! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !!
!! !!!!!! !! !! !! !! !! !!!!!!
!! !! !! !! !! !! !!
!! !! !! !! !! !! !!
!! !!!!!! !! !! !! !!!!!!
出典: sykes2 - 一行の時計 、 sykes2作者のヒント
いくつかのヒント:デフォルトでコンパイル時の警告はありません。 -Wall
を付けてコンパイルすると、以下の警告が出されます。
sykes2.c:1:1: warning: return type defaults to ‘int’ [-Wreturn-type]
sykes2.c: In function ‘main’:
sykes2.c:1:14: warning: value computed is not used [-Wunused-value]
sykes2.c:1:1: warning: implicit declaration of function ‘putchar’ [-Wimplicit-function-declaration]
sykes2.c:1:1: warning: suggest parentheses around arithmetic in operand of ‘|’ [-Wparentheses]
sykes2.c:1:1: warning: suggest parentheses around arithmetic in operand of ‘|’ [-Wparentheses]
sykes2.c:1:1: warning: control reaches end of non-void function [-Wreturn-type]
難読化を解除しましょう。
字下げ
main(_) {
_^448 && main(-~_);
putchar(--_%64
? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >> ";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1
: 10);
}
この混乱を解消するための変数を紹介します。
main(int i) {
if(i^448)
main(-~i);
if(--i % 64) {
char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
putchar(32 | (b & 1));
} else {
putchar(10); // newline
}
}
2の補数のため、-~i == i+1
に注意してください。したがって、私たちは持っています
main(int i) {
if(i != 448)
main(i+1);
i--;
if(i % 64 == 0) {
putchar('\n');
} else {
char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
putchar(32 | (b & 1));
}
}
さて、 a[b]
はb[a]
と同じであり、-~ == 1+
の変更をもう一度適用します。
main(int i) {
if(i != 448)
main(i+1);
i--;
if(i % 64 == 0) {
putchar('\n');
} else {
char a = (">'txiZ^(~z?"-48)[(__TIME__-i/8%8)[7]] + 1;
char b = a >> ";;;====~$::199"[(i*2&8)|i/64]/(i&2?1:8)%8;
putchar(32 | (b & 1));
}
}
再帰をループに変換し、もう少し単純化してこっそりスニークします。
// please don't pass any command-line arguments
main() {
int i;
for(i=447; i>=0; i--) {
if(i % 64 == 0) {
putchar('\n');
} else {
char t = __TIME__[7 - i/8%8];
char a = ">'txiZ^(~z?"[t - 48] + 1;
int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
if((i & 2) == 0)
shift /= 8;
shift = shift % 8;
char b = a >> shift;
putchar(32 | (b & 1));
}
}
}
これは反復ごとに1文字を出力します。 64文字ごとに改行を出力します。それ以外の場合は、何を出力するかを判断するために一対のデータテーブルを使用し、文字32(スペース)または文字33(!
)のいずれかを配置します。最初のテーブル(">'txiZ^(~z?"
)は各文字の外観を説明する10個のビットマップのセットで、2番目のテーブル(";;;====~$::199"
)はビットマップから表示する適切なビットを選択します。
2番目のテーブルint shift = ";;;====~$::199"[(i*2&8) | (i/64)];
を調べることから始めましょう。 i
が4、5、6、または7 mod 8の場合、i/64
は行番号(6から0)で、i*2&8
は8です。
if((i & 2) == 0) shift /= 8; shift = shift % 8
は、テーブル値の上位8進数字(i%8
= 0,1,4,5の場合)または下位8進数字(i%8
= 2,3,6,7の場合)のいずれかを選択します。シフトテーブルは、次のようになります。
row col val
6 6-7 0
6 4-5 0
6 2-3 5
6 0-1 7
5 6-7 1
5 4-5 7
5 2-3 5
5 0-1 7
4 6-7 1
4 4-5 7
4 2-3 5
4 0-1 7
3 6-7 1
3 4-5 6
3 2-3 5
3 0-1 7
2 6-7 2
2 4-5 7
2 2-3 3
2 0-1 7
1 6-7 2
1 4-5 7
1 2-3 3
1 0-1 7
0 6-7 4
0 4-5 4
0 2-3 3
0 0-1 7
または表形式で
00005577
11775577
11775577
11665577
22773377
22773377
44443377
作成者が最初の2つのテーブルエントリにnullターミネータを使用したことに注意してください(卑劣!)。
これは7
sをブランクとして、7セグメント表示の後に設計されています。そのため、最初のテーブルのエントリは、点灯するセグメントを定義する必要があります。
__TIME__
は、プリプロセッサによって定義された特別なマクロです。これは、プリプロセッサが実行された時刻を含む文字列定数に展開されます。形式は"HH:MM:SS"
です。正確に8文字が含まれていることを確認してください。 0から9のASCIIの値は48から57、:
の値はASCIIの値58です。出力は1行あたり64文字なので、1文字あたり8文字の__TIME__
が残ります。
したがって7 - i/8%8
は現在出力されている__TIME__
のインデックスです(i
を下方に繰り返しているので7-
が必要です)。したがって、t
は出力される__TIME__
の文字です。
a
は、入力t
に応じて、バイナリで次のようになります。
0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000
それぞれの数字は ビットマップ で、7セグメント表示で点灯しているセグメントを表します。文字はすべて7ビットASCIIなので、上位ビットは常にクリアされます。したがって、セグメントテーブルの7
は常に空白として印刷されます。 2番目のテーブルは、7
sをブランクにしたものです。
000055
11 55
11 55
116655
22 33
22 33
444433
したがって、たとえば、4
は01101010
(ビット1、3、5、および6のセット)であり、次のように出力されます。
----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--
実際にコードを理解していることを示すために、この表で出力を少し調整しましょう。
00
11 55
11 55
66
22 33
22 33
44
これは"?;;?==? '::799\x07"
としてエンコードされています。芸術的な目的のために、いくつかの文字に64を追加します(下位6ビットのみが使用されるので、これは出力に影響しません)。これは"?{{?}}?gg::799G"
を与えます(8番目の文字は使われていないので、実際に好きなように作ることができます)。元のコードに新しいテーブルを追加します。
main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}
我々が得る
!! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !!
予想通り。原作ほど堅実に見えるわけではないので、作者が自分のした表を使用することを選択した理由を説明します。
読みやすくするためにこれをフォーマットしましょう。
main(_){
_^448&&main(-~_);
putchar((--_%64) ? (32|-(~7[__TIME__-_/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[_*2&8|_/64]/(_&2?1:8)%8&1):10);
}
そのため、引数なしで実行すると、_(従来はargc)は1
となります。 main()
は自分自身を再帰的に呼び出し、-(~_)
の結果(_
の否定的なビットごとのNOT)を渡します。したがって、実際には448回の再帰になります(_^448 == 0
の場合のみ条件付き)。
それを考えて、それは7つの64文字の幅の行を印刷するでしょう(外側の三項条件、そして448/64 == 7
)。それでは少しきれいに書き換えましょう。
main(int argc) {
if (argc^448) main(-(~argc));
if (argc % 64) {
putchar((32|-(~7[__TIME__-argc/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[argc*2&8|argc/64]/(argc&2?1:8)%8&1));
} else putchar('\n');
}
現在、ASCIIスペースの32
は10進数です。スペースまたは '!'を印刷します。 (33は '!'なので最後の '&1
'です)真ん中のブロブに注目しましょう。
-(~(7[__TIME__-argc/8%8][">'txiZ^(~z?"-48]) >>
(";;;====~$::199"[argc*2&8|argc/64]) / (argc&2?1:8) % 8
別のポスターが言ったように、__TIME__
はプログラムのコンパイル時間であり、文字列なので、双方向の配列添字を利用するのと同様に、文字列演算が行われています。a[b]はb [a]と同じです。 ]文字配列の場合.
7[__TIME__ - (argc/8)%8]
これは__TIME__
の最初の8文字のうちの1つを選択します。これはそれから[">'txiZ^(~z?"-48]
にインデックスされます(0-9文字は48-57 10進数です)。この文字列内の文字は、それらのASCII値に対して選択されている必要があります。これと同じ文字ASCIIコードの操作は式の中で続けられ、 ''または '!'のいずれかが出力されます。文字のグリフ内の位置によって異なります。
他の解決策に加えて、-~x
はx+1
と同等であるため、~x
は(0xffffffff-x)
と同じです。これは2の補数の(-1-x)
に等しいので、-~x
は-(-1-x) = x+1
です。
モジュロ演算をできる限り難読化解除し、再帰を削除しました
int pixelX, line, digit ;
for(line=6; line >= 0; line--){
for (digit =0; digit<8; digit++){
for(pixelX=7;pixelX > 0; pixelX--){
putchar(' '| 1 + ">'txiZ^(~z?"["12:34:56"[digit]-'0'] >>
(";;;====~$::199"[pixel*2 & 8 | line] / (pixelX&2 ? 1 : 8) ) % 8 & 1);
}
}
putchar('\n');
}
もう少し拡張します。
int pixelX, line, digit, shift;
char shiftChar;
for(line=6; line >= 0; line--){
for (digit =0; digit<8; digit++){
for(pixelX=7;pixelX >= 0; pixelX--){
shiftChar = ";;;====~$::199"[pixelX*2 & 8 | line];
if (pixelX & 2)
shift = shiftChar & 7;
else
shift = shiftChar >> 3;
putchar(' '| (">'txiZ^(~z?"["12:34:56"[digit]-'0'] + 1) >> shift & 1 );
}
}
putchar('\n');
}