Obfuscated C is a style of writing C code in which the source is almost unreadable, yet the program does something useful. Closely related is the International Obfuscated C Code Contest. It's actually really tasty.

A friend of mine wrote a piece of obfuscated C with lots of #defines, and I could not stop laughing upon reading it. The thing was, he had the #define stuff so well-planned, that when reading the code aloud you couldn't help but laugh. One of the for loops looked like this, for instance:


l33t(idiot q=0; q eats shit; q++)

As one of my inefficient hello world programs, I wrote a program where the only code that isn't preprocessor directives is:
q foo lal lol rod b n s 19 y x n g r w r x a lal n lol x bar l x rofl

Does This Code Make Me Look Obfuscated?

Okay, last year, when I was but a wee Freshman in High School, during the late spring I was just about done with my Intro To Unix course, and I using Linux like a fanatic. I went up to my programming teacher (one guy taught Unix, C, C++, Data Structures, Assembly, and HTML) and asked him if I could skip Intro to Programming and C I and go right on ahead to C II. He said "Sure, knock yourself out". So I spent that summer learning to code C on my own. Well, come last fall, I coded nothing like the other students. At first it was just a format thing, but I got a program off freshmeat to help me out. Anyway, I still look at problems differently then my teacher. For instance, we recently had an argument over white space removal from a string.

He coded it this way, using two increments (in retrospect, I think I would use this way anyway):

while (str[i] != '\0') {
  if (str[j] == ' ') {
    ++j;
    continue;
  }
  str [i++] = str[j++]
}


And I coded the same problem using one increment, and lots of strcpys. Given that I only had strings of 128 characters, I figured it wouldn't be too much extra work for the program. My teacher didn't think it could be done. Here's how I coded it:

while (str[i] != '\0') {
  if (str[i] == ' ') {
    strcpy( str + i, str + (i + 1) );
    continue;
  }
  i++;
}

I know, it's probably not the way the pros do it. But believe it or not, after clocking both versions of code 100,000 times, my teacher's code was a second faster in Real Time, but my code had better User Time and System Time. Very weird.

Update: Thanks Azure Monk for benching the code, I was about to do it myself but it looks like your benchmark is better then the crude ones I was making. None the less, I find it kind of hard to believe that over all the recursive function was the fastest method most frequently (my teacher's code performed the best over all, however). Ahh... it definitely looks prettiest in perl anyway...
munificent: My teachers code would behave that way if it weren't for the fact that when str + j yields a whitespace it just increments j and continues to loop. In Azure Monks code str is returned to its original memory location after the loop finishes (it goes its original length - the amount of whitespace removed back in memory, but it would help if it weren't so obfuscated ;-).

Does This Code Make Me Look Obfuscated?

len = strlen(str);
i = 0;
while(str[0]) {
   while(str[i] == ' ') {
      i++;
      }
   str[0] = str[i];
   str++; 
}
str -= len - i;


Obfuscated enough? I'd love to hear how this stacks up relative to the other two (or if it even works correctly, for that matter). It probably isn't much of an improvement over the other two methods (if it's an improvement at all), but it does it with one (incrementing) variable and without strcpy. Also, i ends up holding the value of the total white space removed, for what it's worth.

This isn't how I'd tend to solve the problem if I came upon it on my own, though.. I'd use two strings, cycle through the source string and copy non-white characters, and then return the second string or strcpy it into the first.




RESULTS:

I ran the four algorithms (A: Teacher's, B: XCthu's, C: Mine, and D: muni's) repeatedly for five seconds with a variety of input.

All algorithms were successfuly able to remove all whitespace from all strings.

String: 'The quick brown fox jumps over the lazy dog.'
A: 1.49 Million
B:  .98
C: 1.42
D: 1.44

String: 'A B'        String: ' '           String: 'AAAAA etc.' (~1024 long)
A: 8.23 Million      A: 10.39 Million      A:   85.36 Thousand
B: 7.77              B:  9.85              B:  107.69
C: 7.13              C:  8.65              C:   77.54
D: 8.38              D: 10.51              D:   78.55

String: 'i  a m  t h e  v e r y  etc.'     String: 'AAAAA etc.' (~128 long)
A: 628.15 Thousand                         A:  667.29 Thousand
B: 138.16                                  B:  830.62
C: 625.33                                  C:  602.97
D: 655.06                                  D:  616.14

String: 'this is a string with normal words etc.' (~1024 long)
A: 80.22 Thousand
B:  5.13
C: 78.83
D: 78.34

String: 'a           b       c              etc.' (~1024 long)
A: 55.88 Thousand
B:  1.02
C: 58.16
D: 58.92
Run on a G4/400. There was lots of overhead for setting up, measuring, and recording the results, so the functions could be expected to go much faster in actual use.

The tests tested strings with 'normal' spacing (like a text paragraph), strings with no spacing, and strings with vast spaces.

Fastest algorithms for each string are marked in bold. Second fastest is in italics.

munificent: Algorithm A correctly removes long strings of whitespace, and Algorithm C makes 'str' point to the beginning of the string with the last line.

eos: I ran yours through a few tests. It runs about 1/3 the speed of A, C, and D in nearly solid strings, 1/2 the speed in normaly spaced strings... and easily twice as fast as anything else where spaces are the predominant character. Nice.

Does This Code Make Me Look Obfuscated?

Your code may perform faster if (and this is unlikely) large memory copies are optimized by your computer. In other words, it may be faster to copy a bunch of characters at a time than each character individually. However, this is pretty unlikely. There is one serious flaw in your code that you should be aware of. According to the C specification, calling strcpy() when the source and destination strings overlap in memory is undefined. This means that on certain compilers this could trash your string, write into bad memory, crash the process or any other of a variety of unpredictable things.

Your teacher's code will work correctly, personally, I try to avoid using continue. The Azure Monk's code example works as well, but uses pointer math, which I also find hard to read. The following is about as readable as I could make it:

int i = 0;
int j = 0;
while( str[ i ] != '\0' )
{
    while( str[ j ] == ' ' )
    {
        j++;
    }
    str[ i++ ] = str[ j++ ];
}

// note: at this point j - i will give the
// number of whitespace characters removed

Does This Code Make Me Look Obfuscated?

void strip(char * newstr, char * oldstr)
{
  for (;*oldstr == ' ';oldstr++);
  *newstr = *oldstr;
  if (*oldstr)
    strip (newstr+1, oldstr+1);
  return;
}

Let a little recursion into your life... Everybody needs a little bloat!
To answer your node's question, though, NO, that does not make you look obfuscated. In fact, it's the more obvious of the two examples you present, as it's strings you're working with there and you keep it in those terms, although both are pretty simple. I'd recommend against strcpy for copying one char, though. Simple assignment would be faster...

$str =~ tr/\s//;

#include <stdio.h>
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);
}

The scary thing is, not only does it compile, but it actually, upon being run, outputs the full text of The Twelve Days of Christmas. No, I didn't write this. I stumbled on it almost by accident on some computer science student's public_html. And no, I don't have any clue how it works (well, I sort of do, but just looking at it for more than 10 seconds makes my head throb).

Log in or register to write something here or to contact authors.