Programmare con Arte n° 0
Da MelugWiki.
Obfuscated C
by foobar fiblan
Il C è un gran bel linguaggio e programmare è senza ombra di dubbio un'arte. Vorrei sottoporre alla vostra attenzione piccoli pezzi di codice, mostrando qualcosa di "diverso" da quello che solitamente leggete in giro. Inizio con un classico dell'Obfuscated C. Chi soffre di cuore puo' ultimare la lettura di questa pagina e passare ad altri forse piu' interessanti argomenti, ma chi è in salute è ben invitato a continuare.
GUARDATE QUESTO CODICE, scritto nel 1988 da Brian Westley per l'IOCCC e che vinse il premio Best Layout!
/*
* Program to compute an approximation of pi
* by Brian Westley, 1988
*/
#define _ -F<00||--F-OO--;
int F=00,OO=00;
main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
_-_-_-_
_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_
_-_-_-_
}
Bello e semplice no? Come potete ben leggere, dal commento iniziale, il programma calcola e stampa un approssimazione del "pi greco".
Non ci credete? Provate pure: aprite una shell, copiate il codice in un file 'pi.c' e compilate, utilizzando l'opzione -traditional-cpp :
$ gcc -traditional-cpp -o pi pi.c $ ./pi
Vi chiederete: Ma che opzione è -traditional-cpp?. Siete proprio degli ignorantoni! Forza, utilizziamo un po' di cervello ragazzi. Sicuramente qualcuno a questo punto proverà a compilare senza l'opzione e si troverà un eseguibile diverso. Ok, vi risparmio una ricerca! Con -traditional-cpp si specifica che dovrà essere utilizzato (emulato) un preprocessore tradizionale (pre-standard). Quindi non ISO! Qualcuno dirà: "Ma non è giusto! A me hanno detto che conviene sempre programmare in ANSI C, utilizzando tutte le opzioni ISO per la compilazione... etc etc..." Io rispondo: "Si certo, hai ragione, è giusto! Ma smettila di leggere quest'articolo perchè qui siamo fuori dagli schemi... vai via!!!" Ora, supponendo che sappiate cosa sia un preprocessore, vi faccio notare come nel codice ci sia una bellissima, libidinosa #define:
#define _ -F<00||--F-OO--;
Prima di proseguire, vi propongo un piccolo esempio per farvi capire, ignorantoni, in maniera abbastanza semplice, una delle differenze del preprocessore tradizionale rispetto a quello ansi.
#define PLUS +
#define INCREMENTA(x) PLUS+x
int main()
{
int a=0;
printf ("Prima: %d\n Dopo: %d\n",a,INCREMENTA(a));
return 0;
}
Compilate con e senza l'opzione -traditional-cpp. Capita la differenza? Allora con il preprocessore pre-standard "tutto" viene sostituito ed in maniera ricorsiva.
Torniamo al codice di Brian Westley e facciamo un po di pulizia nel codice:
- considerando che il cerchio formato da "_-_" ha solo significato artistico-geometrico, infatti ogni istruzione finisce con il punto e virgola (come potete notare alla fine della define), riscriviamo mettendo un'istruzione per riga;
- un' altra osservazione è sui doppi zeri "00" che possono per semplicità essere sostituiti con un solo zero. Il codice con queste modifiche diventa molto piu' leggibile:
#define _ -F<0||--F-OO--;
int F=0,OO=0;
main(){
F_OO();
printf("%1.3f\n",4.*-F/OO/OO);
}
F_OO(){
_
-_
-_
-_
_
-_
-_
-_
-_
-_
-_
-_
-_
_
-_
-_
-_
......
.....
}
NO? non è ancora leggibile? Allora facciamo manualmente quello che dovrebbe fare il "preprocessore pre-standard":
int F=0,OO=0;
main(){
F_OO();
printf("%1.3f\n",4.*-F/OO/OO);
}
F_OO(){
-F<0||--F-OO--;
--F<0||--F-OO--;
--F<0||--F-OO--;
--F<0||--F-OO--;
-F<0||--F-OO--;
--F<0||--F-OO--;
--F<0||--F-OO--;
--F<0||--F-OO--;
--F<0||--F-OO--;
--F<0||--F-OO--;
--F<0||--F-OO--;
--F<0||--F-OO--;
--F<0||--F-OO--;
-F<0||--F-OO--;
--F<0||--F-OO--;
--F<0||--F-OO--;
--F<0||--F-OO--;
......
.....
}
Ora dovrebbe essere piu' comprensibile al vostro void * cervello.
Ci accorgiamo che vengono effettuate delle operazioni sulla variabile F, sulla variabile OO, pre-decrementi e post-decrementi. Si gioca anche sul controllo di F (a volte cambiata di segno, a volte decrementata) per verificare se è minore di zero e solo se questo non è verificato allora viene eseguita la seconda parte "--F-OO--", insomma non occorre sempre un if, per fare certe cose può anche bastare un OR (||).
-F<00|| --F-OO--; --F<00|| --F-OO--;
Interessante anche l'utilizzo della variabile OO che facilmente si confonde con il doppio zero "00", superbo l'utilizzo del nome della funzione F_OO ossia la classica funzione fittizia foo() ma riscritta richiamando elementi 'grafici' del programma, l'underscore "_" e la "OO", insomma veramente un Best Layout.
Alla fine della funzione F_OO() la variabile F varrà esattamente "-201" mentre la variabile OO sarà uguale a -16. Se ora fate:
printf("%1.3f\n",4.*-F/OO/OO);
cioè "4.*-F/OO/OO" vuol dire:
4.00*(-(-201))=804.00 804.00/(-16)=-50.25 (-50.25)/(-16)=3.14
E così viene mandato in output il valore float "3.14" con un printf. E con questo è tutto! Era semplice no?! Alla prossima!