Frage Verschleierter C-Code-Wettbewerb 2006. Bitte erklären Sie sykes2.c


Wie funktioniert dieses C-Programm?

main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

Es kompiliert wie es ist (getestet am gcc 4.6.3). Es druckt die Zeit beim Kompilieren. Auf meinem System:

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

Quelle: sykes2 - Eine Uhr in einer Zeile, sykes2 Autorenhinweise

Einige Hinweise: Standardmäßig keine Kompilierungswarnungen. Kompiliert mit -WallDie folgenden Warnungen werden ausgegeben:

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]

917
2018-03-13 18:22


Ursprung


Antworten:


Lasst uns es ent-verschleiern.

Einrücken:

main(_) {
    _^448 && main(-~_);
    putchar(--_%64
        ? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >> ";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1
        : 10);
}

Einführung von Variablen, um dieses Durcheinander zu entwirren:

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

Beachten Sie, dass -~i == i+1 wegen Zweier-Komplement. Deshalb haben wir

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

Nun beachte das a[b] ist das gleiche wie b[a]und wenden Sie die -~ == 1+ wieder ändern:

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

Konvertieren der Rekursion in eine Schleife und ein wenig mehr Vereinfachung:

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

Dies gibt ein Zeichen pro Iteration aus. Jedes 64. Zeichen gibt eine neue Zeile aus. Andernfalls verwendet es ein Paar von Datentabellen, um herauszufinden, was ausgegeben werden soll, und setzt entweder das Zeichen 32 (ein Leerzeichen) oder das Zeichen 33 (a !). Die erste Tabelle (">'txiZ^(~z?") ist eine Menge von 10 Bitmaps, die das Aussehen jedes Zeichens beschreiben, und die zweite Tabelle (";;;====~$::199") wählt das geeignete Bit aus, das aus der Bitmap angezeigt werden soll.

Die zweite Tabelle

Beginnen wir mit der zweiten Tabelle, int shift = ";;;====~$::199"[(i*2&8) | (i/64)];. i/64 ist die Zeilennummer (6 bis 0) und i*2&8 ist 8 wenn i ist 4, 5, 6 oder 7 mod 8.

if((i & 2) == 0) shift /= 8; shift = shift % 8 wählt entweder die hohe Oktalzahl (für i%8 = 0,1,4,5) oder die niedrige Oktalziffer (für i%8 = 2,3,6,7) des Tabellenwerts. Die Verschiebungstabelle sieht schließlich so aus:

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

oder in Tabellenform

00005577
11775577
11775577
11665577
22773377
22773377
44443377

Beachten Sie, dass der Autor den Nullabschluss für die ersten beiden Tabelleneinträge verwendet hat (hinterhältig!).

Dies ist nach einer Sieben-Segment-Anzeige mit ausgelegt 7s als Leerzeichen. Daher müssen die Einträge in der ersten Tabelle die Segmente definieren, die beleuchtet werden.

Die erste Tabelle

__TIME__ ist ein spezielles Makro, das vom Präprozessor definiert wird. Es wird in der Form in eine Zeichenfolgenkonstante erweitert, die den Zeitpunkt enthält, zu dem der Präprozessor ausgeführt wurde "HH:MM:SS". Beachten Sie, dass es genau 8 Zeichen enthält. Beachten Sie, dass 0-9 die ASCII-Werte 48 bis 57 und haben : hat ASCII-Wert 58. Die Ausgabe beträgt 64 Zeichen pro Zeile, so dass 8 Zeichen pro Zeichen übrig bleiben __TIME__.

7 - i/8%8 ist also der Index von __TIME__ das wird derzeit ausgegeben (die 7- wird benötigt, weil wir iterieren i nach unten). Damit, t ist der Charakter von __TIME__ ausgegeben werden.

a endet abhängig von der Eingabe in Binärform t:

0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000

Jede Zahl ist a Bitmap Beschreibung der Segmente, die in unserer Sieben-Segment-Anzeige beleuchtet sind. Da die Zeichen alle 7-Bit-ASCII sind, wird das High-Bit immer gelöscht. So, 7 in der Segmenttabelle wird immer als Leerzeichen gedruckt. Die zweite Tabelle sieht folgendermaßen aus 7s als Leerzeichen:

000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  

Also zum Beispiel 4 ist 01101010 (Bits 1, 3, 5 und 6 gesetzt), die als druckt

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

Um zu zeigen, dass wir den Code wirklich verstehen, lassen Sie uns die Ausgabe ein wenig mit dieser Tabelle anpassen:

  00  
11  55
11  55
  66  
22  33
22  33
  44

Dies ist codiert als "?;;?==? '::799\x07". Für künstlerische Zwecke fügen wir einigen Zeichen 64 hinzu (da nur die niedrigen 6 Bits verwendet werden, hat dies keinen Einfluss auf die Ausgabe); das gibt "?{{?}}?gg::799G" (Beachten Sie, dass das achte Zeichen nicht verwendet wird, damit wir es tatsächlich machen können, was wir wollen). Setzen Sie unsere neue Tabelle in den ursprünglichen Code:

main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

wir bekommen

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

genau wie wir es erwartet hatten. Es ist nicht so solide aussehend wie das Original, was erklärt, warum der Autor sich entschieden hat, den Tisch zu benutzen, den er gemacht hat.


1768
2018-03-13 19:46



Lassen Sie uns dies formatieren, um das Lesen zu erleichtern:

main(_){
  _^448&&main(-~_);
  putchar((--_%64) ? (32|-(~7[__TIME__-_/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[_*2&8|_/64]/(_&2?1:8)%8&1):10);
}

Also, läuft es ohne Argumente, _ (argc konventionell) ist 1. main() wird sich selbst rekursiv aufrufen und das Ergebnis von -(~_) (negativ bitweise NICHT von _), so wird es wirklich 448 Rekursionen gehen (Nur Bedingung wo _^448 == 0).

Nimmt man das, wird es 7 64-Zeichen breiten Linien (die äußere ternäre Bedingung und 448/64 == 7). Lasst es uns ein wenig sauberer schreiben:

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

Jetzt, 32 ist Dezimal für ASCII-Raum. Es druckt entweder ein Leerzeichen oder ein '!' (33 ist '!', Daher der '&1' Am Ende). Konzentrieren wir uns auf den Blob in der Mitte:

-(~(7[__TIME__-argc/8%8][">'txiZ^(~z?"-48]) >>
     (";;;====~$::199"[argc*2&8|argc/64]) / (argc&2?1:8) % 8

Wie ein anderes Plakat sagte, __TIME__ ist die Kompilierzeit für das Programm und ist eine Zeichenkette, also gibt es eine gewisse Zeichenkettenarithmetik, und nutzt einen Array-Index, der bidirektional ist: a [b] ist gleich wie b [a] für Zeichenanordnungen.

7[__TIME__ - (argc/8)%8]

Dies wird eines der ersten 8 Zeichen auswählen __TIME__. Dies wird dann indiziert [">'txiZ^(~z?"-48] (0-9 Zeichen sind 48-57 dezimal). Die Zeichen in dieser Zeichenfolge müssen für ihre ASCII-Werte ausgewählt worden sein. Dieselbe Zeichen-ASCII-Code-Manipulation wird durch den Ausdruck fortgesetzt, so dass entweder ein '' oder '!' abhängig von der Position innerhalb der Glyphe des Charakters.


97
2018-03-13 21:11



Hinzufügen zu den anderen Lösungen, -~x entspricht x+1 weil ~x ist äquivalent zu (0xffffffff-x). Das ist gleich (-1-x) in 2s ergänzen, so -~x ist -(-1-x) = x+1.


46
2018-03-14 23:54



Ich habe die Modulo-Arithmetik so weit wie möglich entblendet und die Rekursion entfernt

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

Erweitern Sie es ein bisschen mehr:

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

3
2018-04-21 14:48