難読化されたCコードコンテスト2006。sykes2.cについて説明してください


975

この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-1行の時計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]

6
デバッグ:追加printf("%d", _);の先頭にmainプリント:pastebin.com/HHhXAYdJを
陳腐

整数、型なし変数はすべてデフォルトでint
drahnr

18
ヒントを読みましたか?ioccc.org/2006/sykes2/hint.text
nhahtdh


あなたはこのようにそれを実行した場合、それがクラッシュ:./a.out $(seq 0 447)
SSアン・

回答:


1819

難読化を解除しましょう。

インデント:

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
    }
}

-~i == i+12の補数のために注意してください。したがって、

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文字ごとに改行を出力します。それ以外の場合は、1組のデータテーブルを使用して何を出力するかを判断し、文字32(スペース)または文字33(a !)を配置します。最初のテーブル(">'txiZ^(~z?")は、各文字の外観を記述する10個のビットマップのセットであり、2番目のテーブル(";;;====~$::199")は、ビットマップから表示する適切なビットを選択します。

2番目のテーブル

まず、2番目のテーブルを調べてみましょうint shift = ";;;====~$::199"[(i*2&8) | (i/64)];i/64行番号(6から0)でi*2&8あり、8である場合iは、4、5、6 、または7 mod 8です。

if((i & 2) == 0) shift /= 8; shift = shift % 8テーブル値の上位8進数(i%8= 0、1、4、5の場合)または下位8進数(= 2、3、6、7の場合)を選択しi%8ます。シフトテーブルは次のようになります。

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セグメントディスプレイの後に設計されており、7sをブランクとして使用します。したがって、最初のテーブルのエントリは、点灯するセグメントを定義する必要があります。

最初のテーブル

__TIME__プリプロセッサによって定義された特別なマクロです。これは、プリプロセッサが実行された時刻を含む文字列定数に展開されます"HH:MM:SS"。正確に8文字が含まれていることを確認してください。0〜9にはASCII値48〜57と:ASCII値58があることに注意してください。出力は1行あたり64文字なので、1文字あたり8文字が残り__TIME__ます。

7 - i/8%8したがって、__TIME__現在出力されているそのインデックスです(下方向に7-反復しているため、これが必要ですi)。だから、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番目のテーブルは、7sを空白として次のようになります。

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);}

我々が得る

          !!              !!                              !!   
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
          !!      !!              !!      !!                   
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
          !!              !!                              !!   

予想通り。それはオリジナルほどしっかりした見た目ではありません、それは著者が彼がしたテーブルを使うことを選んだ理由を説明しています。


2
@drahnr-技術的にはa *(逆参照)とa +:P の両方
間違いなく

18
@АртёмЦарионов:約30分ですが、戻ってきてかなり編集しています。私はCを頻繁に使用しており、以前は個人的な関心のためにいくつかのIOCCC解読を行ったことがあります(私が最後に行ったのは、個人的な利益のために、この美しいレイトレーサーでした)。それがどのように機能するか尋ねたい場合は、私は義務付け
させていただき

5
@АртёмЦарионов:約IIRC(レイトレーサージオメトリの理解に費やされた時間もカウントします)。キーワードを使用しないため、このプログラムも非常に賢いです。
nneonneo 2013年

178
C ..アセンブリ言語のすべての能力とアセンブリ言語の読みやすさの組み合わせ
wim

6
詳しくは、Don Libesによる「Obfuscated C and Other Mysteries」をご覧ください。難読化されたCコンテストのエントリを分析することにより、Cのテクニックを教えます。
Chris N

102

読みやすくするためにこれをフォーマットしましょう:

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()自身を再帰的に呼び出し、-(~_)(負のビット単位の否定_)の結果を渡すので、実際には448再帰になります(条件がある場合のみ_^448 == 0)。

それを取り、それは7つの64文字幅の行を出力します(外側の3進条件、および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');
}

現在、32ASCIIスペースは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]

これにより、の最初の8文字のいずれかが選択され__TIME__ます。次に、これにインデックスが付けられます[">'txiZ^(~z?"-48](0〜9文字は10〜48〜57です)。この文字列の文字は、ASCII値として選択されている必要があります。これと同じ文字のASCIIコード操作が式を通じて継続され、結果として ''または '!'が出力されます。キャラクターのグリフ内の位置に応じて。


49

他のソリューションに追加すると、-~xはに等しいx+1ため、に等しく~xなり(0xffffffff-x)ます。これは(-1-x)2秒の補数と同じであり、-~xです-(-1-x) = x+1


5
面白い。私はしばらくの間、〜x == -x-1であることを知っていましたが、その背後にある数学的推論を知りませんでした。
ApproachingDarknessFish

3
Ey、Cole、(-1-x)は(-x-1)と同じです。「修正」する必要はありません!!
Thomas Song

7
誰かが-1338である場合、同じ理由ですが、1337ではありません。–
Andrew Mao

4

可能な限りモジュロ演算を難読化し、再帰を削除しました

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');
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.