3

I saw this in the ioccc.

    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);
    }

It prints out 'hello world!' when compiled with gcc, unsure if it works with visual studio 12. Can somebody explain to me how the hell it works??

user1461607
  • 2,416
  • 1
  • 25
  • 23
  • 2
    It redefines `read` calling `write`. – rsp Jun 08 '13 at 12:47
  • just add proper spacing and line breaks - recover structure. `}read` here function MAIN ends and function READ begins - just add empty lines – Arioch 'The Jun 08 '13 at 12:49
  • Also way easier to read when [formatted](http://ideone.com/soUKAq). – mwerschy Jun 08 '13 at 12:49
  • 3
    @sat please revert the code to its original obfuscated form. If you want to format it - add it as a 2nd part of the question. The large share of the question is de-obfuscating and you just deleted this part! – Arioch 'The Jun 08 '13 at 13:29
  • 1
    This explains it http://stackoverflow.com/questions/3836964/interview-hello-world-explanation – user1461607 Jun 08 '13 at 13:36

3 Answers3

4

Since original code was removed from the question and reformatted one been substituted instead, i start with the code as it was given by the topic starter.

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);}

Start with recovering the structure. Add spaces and line breaks where they belong to. Remember that C assumes int data type when not specified.

int i;

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

/* int */ read( /* int */ j,i,p)
{
  write( j/p + p, i-- - j, i/i );
}

Take note that in C the language:

  1. there is no "character" type. A letter is just a (typically 8 bits) integer. Thus '-' - '-' is zero and '/' / '/' is 1 (just like i/i later)
  2. there is no "string" type - that is just a pointer to "character"
  3. arrays and pointers are the same thing by definition
  4. no-sized (byte-sized) pointers and integers are implicitly converted to each other.

Note: in comments below people argue that in C array are not pointers but are just implicitly typecasted to pointers. That is probably true in theory, yet doesn't seem to have any practical implication for this certain code. But still, a bullet 3 is perhaps formulated incorrectly by book.

Also let us assume that all global variables are zero-initialized.

Thus i["]<i;++i){--i;}"] is 0[pointer to some string], then 1[pointer to some string], etc. Effectively being the i-th character of the string.
It is used in a for-loop at the second parameter place - the condition checker. So actually this can be re-formulated as

for( ; "]<i;++i){--i;}"[i] != '\0'; read... );

or by definition of C for loop

while ( "]<i;++i){--i;}"[i] != 0 ) { read ...; }

It is not a coinsidence, that both lines have equally 14 characters:
"hello, world!\n"
"]<i;++i){--i;}"

That makes it just a loop for each character in string, you could re-formulate it as
for i := 0 to 14-1 do

Glue this all together and the program turns into

int i;

/* int */ main()
{
  for( i=0; i<strlen("hello, world!\n"); i++)
  {
    read( 0, & "hello, world!\n"[i], 1);
  }
}

Is it a bit easier? Now moving to the function itself... and to C file handles. By UNIX standard, the following text files are pre-defined:

  • 0 - STDIN - read from keyboard
  • 1 - STDOUT - print to display/printer/log-file/etc
  • 2 - STDERR - emergency print about error conditions

Thus:

/* int */ read( j = 0, i = pointer-to-letter-to-print, p = 1)
{
  write( j/p + p, // 0/1 + 1  == 1 == STDOUT 
         i-- - j, // == i itself due to j == 0; 
                  // -- is POST-decrement and does not affect the calculation
         i/i /*== 1 as long as i != 0  - meaning print only single character*/ );
}

Gluing this together again:

int i;

int my_read( int j, char * pChar, int p)
{
  write( STDOUT, pChar, 1 );
  pChar--; // does not affect anything, as mentioned above
}

const char msg[] = "hello, world!\n";
int main(void)
{
  for( i=0; i<strlen(msg); i++)
  {
    my_read( 0, &msg[i], 1);
  }
}

Can you figure out how this snippet works now? ;-)

Just add spaces and new lines - the proper code structure formatting - and it becomes easy-peesy :-)

Arioch 'The
  • 15,799
  • 35
  • 62
  • 2
    Arrays and pointers are *not* the same thing. It just happens that arrays can decay into pointers to their first elements (and do so rather eagerly). – Mankarse Jun 08 '13 at 13:34
  • @Mankarse by definition, the name of array is the pointer to its zeroeth element, isn't it ? Can you declare a function like `int fun(char param[20])` in C ? I am a Pascal guy, for me C is only needed to read API and samples, so i may be dizzy in theoretical topics. – Arioch 'The Jun 08 '13 at 13:38
  • 1
    The full rule is "Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.". As for the function definition example, the function is adjusted to become `int fun(char *param)`, and so is equivalent to a function taking a pointer. – Mankarse Jun 08 '13 at 13:46
  • @Mankarse that is not the same function, it can take arrays of any length, not 20 :-). Also, if you cannot use `typedef` to define the type of array and not a pointer, and cannot declare a function taking array-not-pointer parameter, then i cannot count it as a data type, sorry. Also was there even a _Alignof in K&R times ? This rule seem to me some latter invention, with ad hoc closing the holes of original C foundation of PDP CPU opcodes ;-) – Arioch 'The Jun 08 '13 at 13:51
  • 2
    Yes, `_Alignof` is a `C11` addition. Even so, the point remains that even in K&R C, pointers and arrays are not equivalent (in particular, with respect to their behaviour around `sizeof`). As for the function example, actually they *are* equivalent. You can't declare a function taking an `array`, because such functions are always adjusted to take pointers instead. (You *can* write a function that takes a *pointer* to an array, but that is a different thing). This rule is given in `K&R` on pg 205, in the section "External function definitions", and in the `C11` standard in `§6.7.6.3/7`. – Mankarse Jun 08 '13 at 14:11
1
  • The result of '-'-'-' is 0.
  • The result of '/' / '/' is 1.

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

    is actually

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

  • Regarding the loop: for(;i["]<i;++i){--i;}"];

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

    Is actually

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

    Because in C, if you write a[i], it's equivalent to i[a] (*(a+i) == *(i+a))

    So this for loop will continue to progress until it encounter the last character.

Putting it all together, it'll be easy to understand what happens here. (I found more explanation in this link: http://www.cs.washington.edu/education/courses/cse142/97su/josh/obfuscate.html)

Maroun
  • 94,125
  • 30
  • 188
  • 241
  • 1
    `it's equivalent to i[a]` not always - is i is array of double and a is array of bytes - those would be different ;-) – Arioch 'The Jun 08 '13 at 13:22
  • @Arioch'The: If `i` is an array of `double` and `a` is an array of `char`, then both `i[a]` and `a[i]` would be compilation errors. What would be the difference? – Mankarse Jun 08 '13 at 13:41
  • `a` is of `char *` type which can be casted to `void *` and then to `int`, no ? – Arioch 'The Jun 08 '13 at 13:43
  • 1
    @Arioch'The: I'm not aware of any rule like that, and [I can't find](http://ideone.com/aTcQMQ) any compiler that operates like that either. – Mankarse Jun 08 '13 at 13:51
  • Well, modern C++ compilers are changing the game, all-warninga-are-errors an such. However http://ideone.com/FAO1tj - you can see that `i[a]` is no more valid construct for ANY ARRAY and ANY TYPED POINTER type of `i`. Only when `i` is non-array scalar `int` then it compiles. – Arioch 'The Jun 08 '13 at 14:03
  • @Arioch'The .. be that as it may, C != C++ .. except in C itself :-) though in some systems the default for some compilers is indeed `-Werror` (quite annoyingly). – Pryftan May 20 '23 at 13:38
0

Let's first format this a little bit better:

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);
}

Second, we look at what it actually does:

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

is the same as

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

So, no initialization, and as long as the string for with "C code" indexed by i is not zero.

Then comes the update part of a for loop:

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

'-'-'-' is zero just like 'a'-'a' would be. i++ + "hello, world!" is a pointer + offset, and of course updates 'i'. And the final part is divide something by itself, which makes 1.

The final trick is a redefined read function, which calls write:

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

And it uses similar tricks to produce the value for stdout with j/p+p (p = 1 from above, j = 0, so 0/1+1 = 1) and the length of the data to write (1). The character to be written is i-- -j, since we take i before doing --, it has no effect [it's not using the global i, but the local one to read.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227