2013-08-02 3 views
0

J'essayais de comprendre comment fonctionne le programme suivant. C'est une calculatrice scientifique en ligne de commande. La source provient de here. Il semblait assez lisible pour une entrée IOCCC mais apparemment ce n'est pas le cas.Calculatrice scientifique obfusquée. S'il vous plaît expliquer comment cela fonctionne

#include <stdio.h> 
#include <math.h> 
#define clear 1;if(c>=11){c=0;sscanf(_,"%lf%c",&r,&c);while(*++_-c);}\ 
    else if(argc>=4&&!main(4-(*_++=='('),argv))_++;g:c+= 
#define puts(d,e) return 0;}{double a;int b;char c=(argc<4?d)&15;\ 
    b=(*_%__LINE__+7)%9*(3*e>>c&1);c+= 
#define I(d) (r);if(argc<4&&*#d==*_){a=r;r=usage?r*a:r+a;goto g;}c=c 
#define return if(argc==2)printf("%f\n",r);return argc>=4+ 
#define usage main(4-__LINE__/26,argv) 
#define calculator *_*(int) 
#define l (r);r=--b?r: 
#define _ argv[1] 
#define x 

double r; 
int main(int argc,char** argv){ 
    if(argc<2){ 
    puts(
     usage: calculator 11/26+222/31 
     +~~~~~~~~~~~~~~~~~~~~~~~~calculator-\ 
     !       7.584,367) 
     +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ 
     ! clear ! 0 ||l -x l tan I (/) | 
     +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ 
     ! 1 | 2 | 3 ||l 1/x l cos I (*) | 
     +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ 
     ! 4 | 5 | 6 ||l exp l sqrt I (+) | 
     +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ 
     ! 7 | 8 | 9 ||l sin l log I (-) | 
     +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(0 
    ); 
    } 
    return 0; 
} 

Il compile sans avertissement sur gcc 4.7.2 (Linux) avec -Wall. J'ai essayé de le simplifier, mais tout autre changement apporté au formulaire légèrement modifié ci-dessous commence à produire des résultats inattendus (sortie incorrecte).

#include <stdio.h> 
#include <math.h> 
#define clear 1;if(c>=11){c=0;sscanf(_,"%lf%c",&r,&c);while(*++_-c);}\ 
    else if(argc>=4&&!main(4-(*_++=='('),argv))_++;g:c+= 
#define puts(d,e) return 0;}{double a;int b;char c=(argc<4?d)&15;\ 
    b=(*_%__LINE__+7)%9*(3*e>>c&1);c+= 
#define I(d) (r);if(argc<4&&*#d==*_){a=r;r=usage?r*a:r+a;goto g;}c=c 
#define return if(argc==2)printf("%f\n",r);return argc>=4+ 
#define usage main(4-__LINE__/26,argv) 
#define calculator *_*(int) 
#define l (r);r=--b?r: 
#define _ argv[1] 
#define x 

double r; 
int main(int argc,char** argv){ 
    if(argc<2){ 
    puts(
     usage: calculator 11/26+222/31 
     +calculator-\ 
     !       7.584,367) 
     + 
     ! clear ! 0 ||l -x l tan I (/) | 
     + 
     ! 1 | 2 | 3 ||l 1/x l cos I (*) | 
     + 
     ! 4 | 5 | 6 ||l exp l sqrt I (+) | 
     + 
     ! 7 | 8 | 9 ||l sin l log I (-) | 
     +(0); 
    } 
    return 0; 
} 

Quelqu'un peut-il expliquer comment cela fonctionne?

+4

Étudiez la forme prétraite de ce code source, peut-être avec 'gcc -C -E obfusc.c | grep -v '^ #' | indent> obfusc.i' alors regardez à l'intérieur 'obfusc.i' –

+4

Certainement pas. Les entrées de l'IOCCC me prennent au moins deux heures pour analyser (je refactoriserais entièrement le code en essayant de comprendre ce qu'il fait), et la longue explication prendrait probablement un jour pour écrire, et serait complètement hors de portée pour ce site A MON HUMBLE AVIS. – cmaster

+0

Notez la macro '__LINE__', ce qui la rend sensible aux sauts de ligne. Il est utilisé pour identifier les opérateurs '/ * + -'. – ugoren

Répondre

4

Il se développe pour ressembler à ceci:

double r; 
int 
main (int argc, char **argv) 
{ 
    if (argc < 2) 
    { 
     if (argc == 2) 
    printf ("%f\n", r); 
     return argc >= 4 + 0; 
    } 
    { 
    double a; 
    int b; 
    char c = (argc < 4 ? main (4 - 21/26, 
        argv) : *argv[1] * (int) 11/26 + 222/31 + 
      ~~~~~~~~~~~~~~~~~~~~~~~~*argv[1] * (int) -!7.584) & 15; 
    b = (*argv[1] % 21 + 7) % 9 * (3 * 367 >> c & 1); 
    c += +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+!1; 
    if (c >= 11) 
     { 
    c = 0; 
    sscanf (argv[1], "%lf%c", &r, &c); 
    while (*++argv[1] - c); 
     } 
    else if (argc >= 4 && !main (4 - (*argv[1]++ == '('), argv)) 
     argv[1]++; 
    g:c += !0 || (r); 
    r = --b ? r : -(r); 
    r = --b ? r : tan (r); 
    if (argc < 4 && *"/" == *argv[1]) 
     { 
    a = r; 
    r = main (4 - 23/26, argv) ? r * a : r + a; 
    goto g; 
     } 
    c = c | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+!1 | 2 | 3 || (r); 
    r = --b ? r : 1/(r); 
    r = --b ? r : cos (r); 
    if (argc < 4 && *"*" == *argv[1]) 
     { 
    a = r; 
    r = main (4 - 25/26, argv) ? r * a : r + a; 
    goto g; 
     } 
    c = c | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+!4 | 5 | 6 || (r); 
    r = --b ? r : exp (r); 
    r = --b ? r : sqrt (r); 
    if (argc < 4 && *"+" == *argv[1]) 
     { 
    a = r; 
    r = main (4 - 27/26, argv) ? r * a : r + a; 
    goto g; 
     } 
    c = c | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+!7 | 8 | 9 || (r); 
    r = --b ? r : sin (r); 
    r = --b ? r : log (r); 
    if (argc < 4 && *"-" == *argv[1]) 
     { 
    a = r; 
    r = main (4 - 29/26, argv) ? r * a : r + a; 
    goto g; 
     } 
    c = c | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(0); 
    } 
    if (argc == 2) 
    printf ("%f\n", r); 
    return argc >= 4 + 0; 
} 

Il est toujours pas exactement lisible, mais au moins maintenant rien ne vous cache. Vous devriez être capable de faire vos simplifications sans être mordu si mal.

Je ne vais pas entrer dans une explication complète pour la même raison @cmaster dit.