This document goes over that lecture on the obfuscated program that prints our "hello, world!". All the examples compile fine under unix, but you might have to add prototypes and include <stdio.h> to get it to work for MSVC. Regardless, be sure to go over this code because it ties together lots of stuff you've seen in cse142.

The original code looked like:

int i;main(){for(i=0;i["]<i;++i){--i;}"];
read('-'-'-',i+++"hell\
o,world!\n",'/'/'/'));}read(j,i,p){
write(j/p+p,i---j,i/i);}
Yuck! Before we can even begin to read this, we need to format it better (of couse C could care less how it's formatted). If we read the conditional of the for loop to be i["]<i, we get this interpretation:
int i;

main()
{
  for(i=0;i["]<i;++i) {
      --i;
      }"];
      read('-'-'-',i+++"hello,
world!\n",'/'/'/')
      );
}

read(j,i,p)
{
  write(j/p+p,i---j,i/i);
}
We run into problems two lines after the for loop. Where we went wrong is that i["]<i is not the loop conditional: the " marks the beginning of a string. The actual conditional is i["]<i;++i){--i;}"]. That's right, the conditional is an int 'i' being indexed by a string. We'll get to what that means later. Here's the new formatting:
int i;

main()
{
  for(i=0;i["]<i;++i){--i;}"];
		  read('-'-'-',i+++"hello,\
			  world!\n",'/'/'/'))
        ;
}

read(j,i,p)
{
  write(j/p+p,i---j,i/i);
}
Now, let's clean this up a bit. What is '-'-'-'? It's merely the value '-' minus the value '-', which is 0 (just like 'a'-'a'==0). So 0 is the first argument to read and 1 is the third argument. Therefore, read passes 1 as the first and third argument to its call to write.

What is i+++"hello, world!\n"? The +++ is broken into ++ + by the compiler, so this is i++ +"hello, world!\n", which adds i (and increments it) to the string "hello, world!\n".

Finally, lets add the correct input/output types to the read function. Applying all these changes, we get the following code:

int i;

void
main(void)
{
  for(i=0;i["]<i;++i){--i;}"];
		  read(0,i++ +"hello,\
			  world!\n",1))
        ;
}

void
read(int j, int ch, int p)
{
  write(1, ch, 1);
}
Recall that 1 is the special FILE pointer to STDOUT, which is the monitor. The function write is a common function that writes a certain number of characters to a file. The call to write above writes 1 character from the string 'ch' to the monitor.

This is looking better, but what about this integer being indexed by a string in i["]<i;++i){--i;}"]. To understand this, recall that a[i] == *(a + i) == *(i + a) == i[a]. In other words, it doesn't matter if we index an array with an int or index an int with an array (we just happen to do the former because of convention).

So for the next version of the function, I switch this around to look more normal, and I rename read to write_char, since that's really what it does. Also, I move i++ to the update part of the for loop. Now the code looks like:

int i;

void
write_char(char *ch)
{
  write(1, ch, 1);
}

void
main(void)
{
  for(i=0; "]<i;++i){--i;}"[i]; i++){
		write_char("hello, world!\n"
                  + i);
  }
}
This is no longer badly obfuscated, but there are a couple parts that could be clearer. First of all, the conditional "]<i;++i){--i;}"[i] returns the value of the i'th character in the string. This will keep the loop going until it evaluates to false, which will happen when we get to the end of the string and it returns the NULL character. So all this really checks is that i doesn't index to the NULL character. In other words, that i < 15.

The other strange part is passing "hello, world!\n" + i to a function. Actually, this is pretty normal for C, but looks weird when you first see it. Remember that a[i] == *(a + i) which also means &a[i] == a + i. So adding a number 'i' to an array is the same as passing the address of the i'th element. This is unecessary when all we want is to print out the charater (as opposed to changing it), so we don't need to pass the address. Here's the final version:

int i;

void
write_char(char ch)
{
  printf("%c",ch);
}

int
main(void)
{
  for(i=0; i < 15; i++) {
	   write_char("hello, world!\n"[i]);
  }
  return 0;
}
And now this looks just like the C code you're used to.


For your interest, here are some other neat obfuscated C examples:

This one does the self-reproducing tic-tac-toe:

                a(X){/*/X=-             a(X){/*/X=-
                -1;F;X=-                -1;F;X=-
                -1;F;}/*/               -1;F;}/*/
char*z[]={"char*z[]={","a(X){/*/X=-","-1;F;X=-","-1;F;}/*/","9999999999  :-| ",
"int q,i,j,k,X,O=0,H;S(x)int*x;{X+=X;O+=O;*x+1?*x+2||X++:O++;*x=1;}L(n){for(*",
"z[i=1]=n+97;i<4;i++)M(256),s(i),M(128),s(i),M(64),N;X*=8;O*=8;}s(R){char*r=z",
"[R];for(q&&Q;*r;)P(*r++);q&&(Q,P(44));}M(m){P(9);i-2||P(X&m?88:O&m?48:32);P(",
"9);}y(A){for(j=8;j;)~A&w[--j]||(q=0);}e(W,Z){for(i-=i*q;i<9&&q;)y(W|(1<<i++&",
"~Z));}R(){for(k=J[*J-48]-40;k;)e(w[k--],X|O);}main(u,v)char**v;{a(q=1);b(1);",
"c(1);*J=--u?O?*J:*v[1]:53;X|=u<<57-*v[u];y(X);K=40+q;q?e(O,X),q&&(K='|'),e(X",
",O),R(),O|=1<<--i:J[*J-48+(X=O=0)]--;L(q=0);for(s(i=0);q=i<12;)s(i++),i>4&&N",
";s(q=12);P(48);P('}');P(59);N;q=0;L(1);for(i=5;i<13;)s(i++),N;L(2);}",0};
                b(X){/*/X=-             b(X){/*/X=-
                -1;F;X=-                -1;F;X=-
                -1;F;}/*/               -1;F;}/*/
int q,i,j,k,X,O=0,H;S(x)int*x;{X+=X;O+=O;*x+1?*x+2||X++:O++;*x=1;}L(n){for(*
z[i=1]=n+97;i<4;i++)M(256),s(i),M(128),s(i),M(64),N;X*=8;O*=8;}s(R){char*r=z
[R];for(q&&Q;*r;)P(*r++);q&&(Q,P(44));}M(m){P(9);i-2||P(X&m?88:O&m?48:32);P(
9);}y(A){for(j=8;j;)~A&w[--j]||(q=0);}e(W,Z){for(i-=i*q;i<9&&q;)y(W|(1<<i++&
~Z));}R(){for(k=J[*J-48]-40;k;)e(w[k--],X|O);}main(u,v)char**v;{a(q=1);b(1);
c(1);*J=--u?O?*J:*v[1]:53;X|=u<<57-*v[u];y(X);K=40+q;q?e(O,X),q&&(K='|'),e(X
,O),R(),O|=1<<--i:J[*J-48+(X=O=0)]--;L(q=0);for(s(i=0);q=i<12;)s(i++),i>4&&N
;s(q=12);P(48);P('}');P(59);N;q=0;L(1);for(i=5;i<13;)s(i++),N;L(2);}
                c(X){/*/X=-             c(X){/*/X=-
                -1;F;X=-                -1;F;X=-
                -1;F;}/*/               -1;F;}/*/



Here's another one I like. It prints out the "12 days of Christmas song".

main(t,_,a )
char
*
a;
{
                                return!
0<t?
t<3?

main(-79,-13,a+
main(-87,1-_,
main(-86, 0, a+1 )

+a)):

1,
t<_?
main( t+1, _, a )
:3,

main ( -94, -27+t, a )
&&t == 2 ?_
<13 ?

main ( 2, _+1, "%s %d %d\n" )

:9:16:
t<0?
t<-72?
main( _, t,
"@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l,+,/n{n+,/+#n+,/#;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# ){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c ;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# }'+}##(!!/")
:
t<-50?
_==*a ?
putchar(31[a]):

main(-65,_,a+1)
:
main((*a == '/') + t, _, a + 1 )
:

0<t?

main ( 2, 2 , "%s")
:*a=='/'||

main(0,

main(-61,*a, "!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m .vpbks,fxntdCeghiry")

,a+1);}



Here's one that prints out an exchange between lovers. It prints

westley
loves me
westley
loves me, not

forever. Try reading the source code (this proves that good code reads like literature):

char*lie;

        double time, me= !0XFACE,

        not; int rested,   get, out;

        main(ly, die) char ly, **die ;{

            signed char lotte,


dear; (char)lotte--;

        for(get= !me;; not){

        1 -  out & out ;lie;{

        char lotte, my= dear,

        **let= !!me *!not+ ++die;

            (char*)(lie=


"The gloves are OFF this time, I detest you, snot\n\0sed GEEK!");

        do {not= *lie++ & 0xF00L* !me;

        #define love (char*)lie -

        love 1s *!(not= atoi(let

        [get -me?

            (char)lotte-


(char)lotte: my- *love -

        'I'  -  *love -  'U' -

        'I'  -  (long)  - 4 - 'U' ])- !!

        (time  =out=  'a'));} while( my - dear

        && 'I'-1l  -get-  'a'); break;}}

            (char)*lie++;


(char)*lie++, (char)*lie++; hell:0, (char)*lie;

        get *out* (short)ly   -0-'R'-  get- 'a'^rested;

        do {auto*eroticism, 

        that; puts(*( out

            - 'c'

-('P'-'S') +die+ -2 ));}while(!"you're at it");


for (*((char*)&lotte)^=

        (char)lotte; (love ly) [(char)++lotte+

        !!0xBABE];){ if ('I' -lie[ 2 +(char)lotte]){ 'I'-1l ***die; }

        else{ if ('I' * get *out* ('I'-1l **die[ 2 ])) *((char*)&lotte) -=

        '4' - ('I'-1l); not; for(get=!


get; !out; (char)*lie  &  0xD0- !not) return!!

        (char)lotte;}


(char)lotte;

        do{ not* putchar(lie [out
        
        *!not* !!me +(char)lotte]);
            
        not; for(;!'a';);}while(

            love (char*)lie);{


register this; switch( (char)lie

        [(char)lotte] -1s *!out) {

        char*les, get= 0xFF, my; case' ':

        *((char*)&lotte) += 15; !not +(char)*lie*'s';

        this +1s+ not; default: 0xF +(char*)lie;}}}

        get - !out;

        if (not--)
            
        goto hell;

            exit( (char)lotte);}
There are many more examples, but I'll stop here. Now you can see how tame the example we went over in lecture was!