Reputation: 107
Can someone explain this line by line (character by character maybe, haha)?
typedef int (*funcptr)(); /* generic function pointer */
typedef funcptr (*ptrfuncptr)(); /* ptr to fcn returning g.f.p. */
funcptr start(), stop();
funcptr state1(), state2(), state3();
void statemachine()
{
ptrfuncptr state = start;
while(state != stop)
state = (ptrfuncptr)(*state)();
}
funcptr start()
{
return (funcptr)state1;
}
For example, I would like to know why there are () at the end of line 1. An answer like "that's just how you declare a pointer to a function" would be satisfying, but then when you declare a variable of that type, why only use "funcptr" without the ()?
Lines 4 and 5. Why do you have the () here? Those aren't functions, they're pointers to functions, right?
Line 9. Why doesn't "state" have ()? Still a pointer to a function as are the ones on lines 5 and 6.
Line 9. What is "start" without the ()?
Line 12. WHAT?! (I know what typecasting is. At least I think I do...)
Line 17. Why does "state1" require a typecast? It is of the type it's being cast as already. Or is it because it's missing the ()?
It would really help me to understand these concepts.
PS. This is for a microcontroller I'll use in an electronic Dummy Load I'm designing. Figured it's a good opportunity to learn more about C. The code is from http://c-faq.com/decl/recurfuncp.html
Upvotes: 4
Views: 921
Reputation: 753585
As noted in the question, this comes from the C FAQs web site. The question is:
Q: How can I declare a function that can return a pointer to a function of the same type? I'm building a state machine with one function for each state, each of which returns a pointer to the function for the next state. But I can't find a way to declare the functions—I seem to need a function returning a pointer to a function returning a pointer to a function returning a pointer to a function…, ad infinitum.
A: You can't quite do it directly. One way is to have the function return a generic function pointer (see question 4.13), with some judicious casts to adjust the types as the pointers are passed around:
And then there's a first example using the code shown in the SO question.
As the FAQ answer says, you can't create a function that returns a pointer to its own type of function, so you have to bludgeon the compiler into working.
typedef int (*funcptr)();
This has the ()
at the end because without them, you'd have typedef int (*intptr);
or typedef int *intptr;
which is not what you want. The empty parentheses are an indeterminate — not empty — list of arguments. It is the way you declare a function pointer — before even trying to compile with my default compilation options, I modified the code to: typedef int (*funcptr)(void);
.
A funcptr
, therefore, is a pointer to a function that returns an int
and (at least for the purposes of this discussion) takes no arguments.
typedef funcptr (*ptrfuncptr)();
Don't try this without the intermediate type! This too is a pointer to a function, and the function returns a funcptr
— and I used typedef funcptr (*ptrfuncptr)(void);
to assert 'and takes no arguments'.
funcptr start(), stop();
etcThese lines declare a set of 5 functions. Again, the argument lists are unspecified — so I'm going to treat them as having (void)
. These functions return a funcptr
. However, their own type is not funcptr
. This is the point made in the answer.
Indeed, treated as a name (without the parentheses), the type of start
, stop
, and state1
through state3
is ptrfuncptr
— pointer to a function returning a funcptr
.
ptrfuncptr state = start;
The variable state
is of type ptrfuncptr
, and is initialized (without need for casting) to point at the function start
. Note that this does not call the function; it merely initializes a variable, just as if you have int i = -37;
, it initializes a variable i
of type int
to the value -37
.
state = (ptrfuncptr)(*state)();
Time to get the bludgeons out. This line contains a function call and a cast.
The original logic behind function pointers was the 'type mimics use' concept. For example, if you have:
int *p;
then in an expression, *p
has the type int
. With function pointers, you have:
int (*intfuncptr)();
and in an expression, (*intfuncptr)()
represents an int
; it is the result of invoking the function pointed at by intptrfunc
. In pre-standard C, the (*pointer_to_function)()
notation was the only way to use a pointer to function. Standard C allows you to omit the (*
and )
around the pointer.
Thus, in modern notation, the line state = (ptrfuncptr)(*state)();
could also be written state = (ptrfuncptr)state();
. When I learned C, this wasn't an option, so I still prefer the explicit 'this is invoking a function via a pointer to function' notation. The FAQ does mention this.
So, the line calls the function pointed to by state
, and captures the return value in state
. But the value returned by the function is a funcptr
, not a ptrfuncptr
, so we need to bludgeon the compiler into accepting that we know enough of what we're doing to remain silent. So, the (ptrfuncptr)
cast does that.
return (funcptr)state1;
Since start
returns a funcptr
, but state1
is a pointer to a function that returns a funcptr
, the cast here is, once more, necessary to bludgeon the compiler into accepting the type mismatch. Without parentheses after it, state1
is just the name of the function, not an invocation of the function, and therefore has the type ptrfuncptr
— pointer to a function returning a funcptr
, not just funcptr
which is what start
is supposed to return. So, the cast is necessary.
For more mind-blowing function pointers, see:
typedef
works for function pointerstypedef
pointers? — general answer, no, but there's a strong exception for pointers to functions.…and somewhere there's a question that discusses trivia like state = (ptrfuncptr)(******state)();
(using notation from this Q&A), and the mulitple stars work too…
Upvotes: 6