1

This is a basic C program and it doesn't use looping or conditions to print digits. I wanna know how it does what it does, along with purpose of "exit" and "main". Is main used for recursion here?

#include <stdio.h>
#include <stdlib.h>

void main(int j) {
    printf("%d\n", j);
    (&main + (&exit - &main)*(j/1000))(j+1);
}
vaibhav
  • 396
  • 2
  • 17
  • 7
    This is a basic wrong-in-many-ways C program using recursion with the `main` function ... – Chnossos May 17 '14 at 18:50
  • It uses a horrible hacky pointer-based recursion. So no loops, but lots of hidden-in-plain-sight calls to main() – Marc B May 17 '14 at 18:52
  • its a very cool version of recursion. the multiplication is like a comparison and redirects to the exit function if j is 1000 – user287107 May 17 '14 at 18:56
  • Note that the function pointers do not need the `&` in front; the function call could have been written as: `(main + (exit - main)*(j/1000))(j+1);`. In fact, you can even add `*`s to the code: `(*main + (**exit - ***main)*(j/1000))(j+1);`. It is interesting that because `exit()` is `void exit(int)`, the code with the dubious definition of `main()` doesn't have warnings from the pointer subtraction. But the code is so nasty that … it has gotten far more attention than it warranted. – Jonathan Leffler May 17 '14 at 19:25
  • 1
    The exact problem with this code in Standard C is that it is a constraint violation because `-` and `+` cannot be used with function pointers. Also, of course, it's implementation-defined as to whether this signature of `main` is permitted. – M.M May 18 '14 at 05:07
  • 2
    possible duplicate of [How does the C code that prints from 1 to 1000 without loops or conditional statements work?](http://stackoverflow.com/questions/7937789/how-does-the-c-code-that-prints-from-1-to-1000-without-loops-or-conditional-stat) – phuclv Jul 21 '15 at 09:44

4 Answers4

5

Suppose that j is 500. Then (&exit - &main) is something (doesn't matter what) and (j/1000) is 0, so (&main + (&exit - &main)*(j/1000))(j+1) is effectively main(501). In contrast, if j is 1000, then (j/1000) is 1, which means (&main + (&exit - &main)*(j/1000)) is the same as (&main + (&exit - &main)) (which is to say, &exit), so it calls exit(1001) instead.

Sneftel
  • 40,271
  • 12
  • 71
  • 104
  • 3
    And all hell breaks loose if the program is initially invoked with 2000 or more arguments. Or, at least, the behaviour is even more undefined than it is the rest of the time. – Jonathan Leffler May 17 '14 at 18:56
  • 1
    @JonathanLeffler True, but that doesn't even make the top 5 in the list of "reasons this is not good code". ;) – Sneftel May 17 '14 at 19:07
  • Agreed; the code is egregiously awful, and providing 2000 arguments to a program is an entertaining exercise: `./a.out $(perl -e 'print "A " x 2000')`. You could sort of fix the indeterminacy by using: `(&main + (&exit - &main)*((j % 1000 + 1)/1000)))`, but the program probably crashes when the recursion goes too deep if `j` is greater than 1000 when first called. All of which applies far too much analysis to something abominable. – Jonathan Leffler May 17 '14 at 19:20
  • @JonathanLeffler not necessarily: only `int main(void)` and `int main(int, char **)` have their behaviour specified by the standard. If the implementation defines behaviour for `void main(int)` then it is not required that the parameter be the number of commandline arguments – M.M May 18 '14 at 05:10
4

I suggest it is supposed to work as follow:

  • when j < 1000, call &main + (&exit - &main)*(j/1000) with argument j+1. Since j/1000 == 0, it calls main(j+1).
  • when j = 1000, since j/1000 == 1, it calls &main + (&exit - &main) * 1 == exit.

However, there are a lot of errors in this tiny C program. For instance, void main(int) is not a standard main signature.

md5
  • 23,373
  • 3
  • 44
  • 93
  • 1
    Beware of Microsoft — see [What should `main()` return in C and C++?](http://stackoverflow.com/questions/204476/what-should-main-return-in-c-and-c/18721336#18721336). – Jonathan Leffler May 17 '14 at 18:58
  • +1 for warning about the main function prototype along with the answer to the actual question. – Chnossos May 17 '14 at 18:59
  • @JonathanLeffler: Well, I wasn't aware of this behavior. Does MS accepts a single argument too? – md5 May 17 '14 at 19:01
  • No; not officially — no compiler that I know of strictly accepts just one `int` argument to `main`. In practice, extra arguments are usually just ignored by the called function — so it works, but the standard doesn't guarantee that it will work. – Jonathan Leffler May 17 '14 at 19:03
3

This one actually compiles to assembly that doesn't have any conditionals:

#include <stdio.h>
#include <stdlib.h>

void main(int j) {
  printf("%d\n", j);
  (&main + (&exit - &main)*(j/1000))(j+1);
}

Try: Added '&' so it will consider the address hence evading the pointer errors.

This version of the above is in standard C since it doesn't rely on arithmetic on function pointers:

#include <stdio.h>
#include <stdlib.h>

void f(int j)
{
    static void (*const ft[2])(int) = { f, exit };

    printf("%d\n", j);
    ft[j/1000](j + 1);
}

int main(int argc, char *argv[])
{
    f(1);
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
3
  • &main is the address of the main function (entry point of any C program)
  • &exit is the address of the exit function (terminates the program)

Consider your statement:

(&main + (&exit - &main)*(j/1000))(j+1);



if j < 1000, then 
 (&exit - &main)*(j/1000) == 0 (integers division)
 // So we are calling main(j+1)
else if j == 1000
 (&exit - &main)*(j/1000) == &exit - &main
 // So we are calling (&main + &exit - &main)(j+1), which is calling exit(j+1)

So your program is calling main() recursively until j == 1000, at which point it calls exit

This program is so wrong in terms of programming/best practices/etc..., that you should just forget about it.

quantdev
  • 23,517
  • 5
  • 55
  • 88