Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the C code that prints from 1 to 1000 without loops or conditional statements work?

Don't ever write code like that.


For j<1000, j/1000 is zero (integer division). So:

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

is equivalent to:

(&main + (&exit - &main)*0)(j+1);

Which is:

(&main)(j+1);

Which calls main with j+1.

If j == 1000, then the same lines comes out as:

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

Which boils down to

(&exit)(j+1);

Which is exit(j+1) and leaves the program.


(&exit)(j+1) and exit(j+1) are essentially the same thing - quoting C99 §6.3.2.1/4:

A function designator is an expression that has function type. Except when it is the operand of the sizeof operator or the unary & operator, a function designator with type "function returning type" is converted to an expression that has type "pointer to function returning type".

exit is a function designator. Even without the unary & address-of operator, it is treated as a pointer to function. (The & just makes it explicit.)

And function calls are described in §6.5.2.2/1 and following:

The expression that denotes the called function shall have type pointer to function returning void or returning an object type other than an array type.

So exit(j+1) works because of the automatic conversion of the function type to a pointer-to-function type, and (&exit)(j+1) works as well with an explicit conversion to a pointer-to-function type.

That being said, the above code is not conforming (main takes either two arguments or none at all), and &exit - &main is, I believe, undefined according to §6.5.6/9:

When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; ...

The addition (&main + ...) would be valid in itself, and could be used, if the quantity added was zero, since §6.5.6/7 says:

For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

So adding zero to &main would be ok (but not much use).


It uses recursion, pointer arithmetic, and exploits the rounding behavior of integer division.

The j/1000 term rounds down to 0 for all j < 1000; once j reaches 1000, it evaluates to 1.

Now if you have a + (b - a) * n, where n is either 0 or 1, you end up with a if n == 0, and b if n == 1. Using &main (the address of main()) and &exit for a and b, the term (&main + (&exit - &main) * (j/1000)) returns &main when j is below 1000, &exit otherwise. The resulting function pointer is then fed the argument j+1.

This whole construct results in recursive behavior: while j is below 1000, main calls itself recursively; when j reaches 1000, it calls exit instead, making the program exit with exit code 1001 (which is kind of dirty, but works).


Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!