0

Here comes a snippet from an old school book: C Traps and Pitfalls, second chapter.

(*(void(*)())0)();

which is equivalent to the more modern:

typedef void (*function_pointer) ();
(*(function_pointer)0)();

I would like some help to better understand what it is with the second code line right below typedef please. How do I explain that weird syntax?

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Mynicks
  • 213
  • 1
  • 9
  • 2
    It is saying "cast `0` into the function pointer type and call this function". This is a trick to jump to the address `0` of the memory, effectively causing a "warm-reset" on some systems. – Eugene Sh. Mar 02 '17 at 18:36
  • 1
    @Jean-FrançoisFabre Thanks :) They sleep, though.. – Eugene Sh. Mar 02 '17 at 18:37
  • That's not "more modern", but just another way to write it, which **might** be better readable (depends on experience of the reader). And both constructs invoke undefined behaviour. And there is nothing"weird" about that; it's just how C is. – too honest for this site Mar 02 '17 at 18:37
  • @EugeneSh.: A null pointer is not required to have a representation of "all bits zero". The code invokes UB wrt the language. Without further information, it is not safe to assume anything else. – too honest for this site Mar 02 '17 at 18:40
  • what is true is that some old CPU use that to reset as Eugene Sh. IIRC 68k processor (on AmigaOS) jumps to address `2` after a `RESET` instruction to reboot the computer. – Jean-François Fabre Mar 02 '17 at 18:43
  • @Olaf This is the gray area between the language pureness and the real-life. This line is definitely supposed to do what I said, and you most likely to know it. In C using the NULL pointer for anything other than indicating the invalid one is a strict no-no, but `0` is a valid memory address sometimes even if it looks like NULL.. – Eugene Sh. Mar 02 '17 at 18:44
  • @EugeneSh. not quite true. [0 is special in a pointer context](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=73) and will always equal `NULL` even on an unusual system where `NULL` is actually something other than all-bits-zero. To actually get pointer-zero, you have to do something weird like `(void*)(1-1)`. – Alex Celeste Mar 02 '17 at 18:47
  • @EugeneSh.: The problem with the integer `0` and pointers is: you cannot use it as the _value_ `0`as it is always converted to a _null pointer_. I don't disagree with you about the **likely** intention. I just stated that there is not enough information to assume it. Nevertheless, I don't see the problem; OP seems to understand what the first variant does, the second is a matter of knowing typecast syntax -> C 101. C2x shoulde eventually provide a new keyword like C++ `nullptr`. To hell with the holy grail "compatibility". – too honest for this site Mar 02 '17 at 18:50
  • 1
    Related: [Why do function pointer definitions work with any number of ampersands '&' or asterisks '*'?](http://stackoverflow.com/questions/6893285/why-do-function-pointer-definitions-work-with-any-number-of-ampersands-or-as) – Lundin Mar 03 '17 at 11:57

1 Answers1

0

Unlike with object pointers (inc. those to arrays), the & and * operators are optional when working with function pointers. Some people like to use them anyway, being of the opinion that it makes the code's use of function pointers clearer. Personally I disagree, and just use the names.

Therefore:

// given
typedef int (* F) (void);
extern int foo (void);

// the following definitions are all identical:
F fp1 = foo;
F fp2 = &foo;
F fp3 = *foo;

// as are the following call expressions:
foo ();
(&foo) ();
(*foo) ();
fp1 ();
(*fp1) ();
(******* fp1) ();

Much like the case for arrays, a function used as a value in any expression always decays to a function pointer, except when used as the immediate argument to &. Technically this also includes function call expressions, so the thing on the left of the parentheses is always a function pointer, even when the call is directly to a statically-named function (i.e. the expression foo ()). Similarly, dereferencing a function pointer with * retrieves a function's "value", but the only thing a function "value" can do is immediately decay back to a pointer, which is why you can dereference it with as many stars as you like.

None of this has any effect at runtime; it only affects the expression's type.

Using a function pointer as the direct argument to & will do something else, however: it will either create a standard double-pointer (a pointer to the variable), or will be syntactically invalid if the function expression is not an lvalue. You can stick one level of & in front of some of those optional stars as well, in which case it prevents the result of the * operator from immediately decaying and instead converts the type explicitly, continuing to achieve absolutely nothing.

So the simpler (and IMO clearer) way to write the code from the question would be as follows:

typedef void (*function_pointer) ();

// 1 - direct
((function_pointer)0)();

// 2 - intermediate variable
function_pointer fp = (function_pointer)0;
fp ();

Most of the stars do nothing, and the code is easier to read without them. This also shows that the second line of the question's expression is actually two operations (both generally no-ops at the machine level) - first it converts 0 to have the type function_pointer, and then it applies the *. (Then in a third no-op, it decays the * away again, outside the parentheses.)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Alex Celeste
  • 12,824
  • 10
  • 46
  • 89