views:

2383

answers:

6

This classic ioccc entry is a hello world program written in c. Can anyone please provide an explanation of how it works?

Original code (syntax highlighting intentionally missing):

int i;main(){for(;i["]<i;++i){--i;}"];read('-'-'-',i+++"hell\
o, world!\n",'/'/'/'));}read(j,i,p){write(j/p+p,i---j,i/i);}

Slightly cleaner:

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

read(j, i, p)
{
  write(j / p + p, i-- - j, i / i);
}
+16  A: 

Not in a mind of taking this apart completely, but there are some hints:

  • '-' - '-' is obfuscated for 0
  • '/' / '/' and the i / i in read() are obfuscated for 1

Remember that [] is commutative, i.e. i["]<i;++i){--i;}"] is the same as "]<i;++i){--i;}"[i] (having a char array and pointing at the i'th element of it). The content of the string doesn't matter in any way, only its length is used to define the number of iterations of the loop. Any other string of the same length (incidentially, the same length as the output...) would work. After that number of iterations, "string"[i] returns the null character that terminates the string. Zero == false, loop terminates.

With that, it should be comparatively easy to figure out the rest.

Edit: The upvotes made me interested enough to look at this some more.

Of course, i++ + "Hello, world!\n" is the same as "Hello, world!\n"[ i++ ].

codelark already pointed out that 0 is the fid for stdout. (Give him +1 for that, not me. Just mentioning it for completeness.)

The i in read() is of course local, i.e. the i-- in read() does not affect the i in main(). Since the following i / i is always 1 anyway, the -- operator does nothing at all.

Oh, and tell the interviewer to fire whoever wrote this code. It does not do error checking on the return value of write(). I can live with notoriously badly-written code (and have, for many years), but not checking for errors is worse than bad, that's faulty. :-)

The rest is just some third-grade maths. I'd pass that to an intern. ;-)

DevSolar
+9  A: 

The string index of i["..."] causes the loop to execute for the length of the string. i++ + the hello world string means each iteration passes the string starting one letter deeper to the local read function.

first iteration = "hello.." second iteration = "ello.."

The read function's first parameter simplifies to 0, stdout's file descriptor. The last parameter simplifies to 1, so only one character is written per call. This means each iteration will write the first character of the string passed to stdout. So following the example strings from above:

first iteration = "h" second iteration = "e"

The string index in the for loop is the same length as the hello world string, and as [] is commutative, and strings are null terminated, the last iteration will return the null character and exit the loop.

codelark
Better than mine. +1.
DevSolar
+1  A: 

Yet another interview question that seems to reflect more on the interviewer than the interviewee. Key issues here: * j is always 0 (('-' - '-') == 0) * p is always 1 (('/' / '/') == 1) * i (in the read function) is (i++ + "hello world") == the i'th character in hello world (then increment i)

Thus the read function becomes

read(0, NextChar, 1)
{
  write(1, NextChar , 1);
}

The only other bit is the commutativity of the []operator in the for loop.

Understanding and parsing this kind of code is really valueless imho.

Elemental
The commutativity of the [] operator is the only *valueable* knowledge in this piece of code IMHO...
DevSolar
@DevSolar - yeah - fair enough; edited my answer to make my meaning clearer
Elemental
When I first encountered this, that was the one thing that really surprised me. Though, I have a c++ background.
JoshD
+4  A: 

Far from C expert, but I'll try:

i["]<i;++i){--i;}"]
// is actually
char* x = "]<i;++i){--i;}"
*(i + x)
// and will turn to zero when i == 14 (will point to string's ending zero)
// '-' - '-' and '/' / '/' are always 0 and 1
// int i is initiated to zero
// i++ will return original value, so main turns to
main()
{
    char* hello = "hello, world!\n";
    for (i = 0 ; i != 14; i++) read(0, hello[i], 1);
}

// read becomes
// i-- does nothing here, i is in read's scope, not the global one
read(j, i, p)
{
  write(1, i, 1);
}

// and at last
main()
{
    char* hello = "hello, world!\n";
    for (i = 0 ; i<14; i++) write(1, hello[i], 1);
}
ymv
+39  A: 

for loop condition

i["]<i;++i){--i;}"]

This expression takes advantage of the fact that array indexing is commutative in C. It is equivalent to.

"]<i;++i){--i;}"[i]

So the loop will terminate when the character at position i is \0, i.e., at the end of the string, which is 14 characters long (which happens to be the same length as "hello, world!\n"). So the for loop condition can be rewritten as:

i != 14

character arithmetic

read('-' - '-', i++ + "hello, world!\n", '/' / '/')

char is an integer type, and thus:

  • '-' - '-' is 0
  • '/' / '/' is 1

    read(0, i++ + "hello, world!\n", 1)


After fixing all the compiler warnings (like implicit int to pointer conversion), and simplifying the things mentioned above, the code becomes:

#include <unistd.h>

int i = 0;

void read2(int, char*, int);

int main()
{
   while (i != 14)
   {
      read2(0, i++ + "hello, world!\n", 1);
   }

   return 0;
}

void read2(int j, char* i, int p)
{
   write(j / p + p, i-- - j, 1);
}

(I renamed read to read2 to avoid conflicting with the Unix read function.)

Note that the j and p arguments to read2 are unneeded, as the function is always called with j=0 and p=1.

#include <unistd.h>

int i = 0;

void read2(char*);

int main()
{
   while (i != 14)
   {
      read2(i++ + "hello, world!\n");
   }

   return 0;
}

void read2(char* i)
{
   write(1, i--, 1);
}

The call write(1, i--, 1) writes 1 character from i to file descriptor 1 (stdout). And the postdecrement is superfluous because this i is a local variable never referenced again. So this function is equivalent to putchar(*i).

Inlining the read2 function within the main loop gives

#include <stdio.h>

int i = 0;

int main()
{
   while (i != 14)
   {
      putchar(*(i++ + "hello, world!\n"));
   }

   return 0;
}

for which the meaning is obvious.

dan04
nice explanation
Nishant
Very precise and elaborate.
DevSolar
Excellent answer!
Niels Basjes
Typo in the third code block:void read2(char* i){ write(1, i--, 1);}Should be:void read2(char* i){ write(0, i--, 1);}Where 0 is the file descriptor for STDOUT.
Jonathan Branam
+2  A: 

The link from the IOCCC page about the person who had it tattooed on their arm does a pretty thorough explanation of how the code works.

elmt