991

この 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]
4

4 に答える 4

1833

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

インデント:

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

2番目のテーブル

2番目のテーブルを調べることから始めましょうint shift = ";;;====~$::199"[(i*2&8) | (i/64)];i/64は行番号(6から0)であり、4、5、6、またはi*2&87mod8の場合iは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つのテーブルエントリにヌルターミネータを使用したことに注意してください(卑劣です!)。

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__は現在出力されているインデックスです(下向き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  

したがって、たとえば、(ビット1、3、5、および6セット)は、次のように出力されます401101010

----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--

コードを本当に理解していることを示すために、次の表を使用して出力を少し調整してみましょう。

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

我々が得る

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

思った通り。それはオリジナルほど堅実に見えません、それは著者が彼がしたテーブルを使うことを選んだ理由を説明します。

于 2013-03-13T19:46:48.970 に答える
104

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

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です。 は再帰的に自分自身を呼び出し、 (の負のビット単位のNOT )main()の結果を渡すので、実際には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');
}

ここで、32はASCIIスペースの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文字の1つが選択され__TIME__ます。次に、これにインデックスが付けられます[">'txiZ^(~z?"-48](0〜9文字は10進数で48〜57です)。この文字列の文字は、ASCII値として選択されている必要があります。この同じ文字のASCIIコード操作は式全体で継続され、''または'!'のいずれかが出力されます。キャラクターのグリフ内の場所によって異なります。

于 2013-03-13T21:11:25.860 に答える
49

-~xは に等しいx+1ため、他の解に追加すると に等しく~xなり(0xffffffff-x)ます。(-1-x)これは、2 の補数で に等しいので、-~xです-(-1-x) = x+1

于 2013-03-14T23:54:55.973 に答える
6

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

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');
}
于 2013-04-21T14:48:57.980 に答える