
How Do I Declare a Function Pointer in C? - jerryr
http://fuckingfunctionpointers.com
======
petters
Just use the typedef. Even if you personally find the other variants readable,
chances are that your peer reading your code doesn't.

~~~
DSMan195276
Personally I don't like when people hide a pointer behind a typedef. If you
want to use a typedef, typedef the function and then declare a pointer to
that:

    
    
        typedef int func(void);
    
        func *func_ptr;
    

Avoids the mess of the function pointer syntax, but still makes the fact that
it is a pointer clear.

~~~
lomnakkus
Correct me if I'm wrong, but isn't a "function" always a pointer in C? That
is, there's no such thing as a "value function" in C, right?

Given that, what's the advantage for "your version" of the idiom?

(This may just be nitpicking.)

~~~
evincarofautumn
One advantage is that you can declare functions with it, which is useful when
you have many different operations of the same type. For example, take a toy
calculator:

    
    
        typedef double binary_operation(double, double);
        binary_operation add, subtract, multiply, divide;
        double add(double a, double b) { return a + b; }
        /* ... */
    
        struct binary_operator {
            char const *name;
            binary_operation *operation;
        } binary_operators[] = {
            { "+", add },
            { "-", subtract },
            { "*", multiply },
            { "/", divide },
            { NULL, NULL },
        };
    

You can also use the function type in parameter lists, but it’s equivalent to
a function pointer type.

    
    
        int atexit(void function(void));

~~~
lomnakkus
Well, yeah, obviously there's less redundancy, but I'm specifically not seeing
the advantage that OP mentioned. (Which is all that I'm questioning.)

~~~
loeg
Here's an example:

    
    
        typedef void (*sighandler_t)(int);
        sighandler_t signal(int signum, sighandler_t handler);
    

Without the typedef, it's much less clear.

Edit: It could instead be written as the non-pointer type:

    
    
        typedef void (sighandler_t)(int);
    

And then used as:

    
    
        sighandler_t *signal(int signum, sighandler_t *handler);
    

And as a function declaration:

    
    
        sighandler_t foo;
    

With the pointer in the typedef, the type can't be used to declare functions.

------
TheAdamist
The new c++ alt function syntax talked about here:
[https://blog.petrzemek.net/2017/01/17/pros-and-cons-of-
alter...](https://blog.petrzemek.net/2017/01/17/pros-and-cons-of-alternative-
function-syntax-in-cpp/)

mentions replacing function declarations for

    
    
      void (*get_func_on(int i))(int); 
    

with

    
    
      auto get_func_on(int i) -> void (*)(int);
    

which looks a lot more readable to me.

~~~
Longhanks
I'd say this is even more readable:

    
    
      auto get_func_on() -> std::function<void(int)>

~~~
_lm_
It's more readable, but using std::function here introduces a second layer of
indirection vs using a plain function pointer.

More specifically, std::function's operator() is virtual, and calls into a
subclass that's specialized to function pointers of type void(int). The
subclass then performs the actual function pointer call.

~~~
gpderetta
Technically function::operator() is not virtual (which wouldn't be very useful
as std::function has value semantics), but it does runtime dispatching
internally using an unspecified mechanism.

This can be virtual functions, or, more commonly, an hand rolled vtable. In
the last case, if std::function is constructed with a function pointer exactly
matching its signature it could in principle avoid the thunk and directly
point to the function itself. I don't think most implementations bother.

/pedantic

------
dnquark
The trick to reading crazy C declarations is learning the "spiral rule":
[http://c-faq.com/decl/spiral.anderson.html](http://c-faq.com/decl/spiral.anderson.html)
(here are more examples, with nicer formatting:
[http://www.unixwiz.net/techtips/reading-
cdecl.html](http://www.unixwiz.net/techtips/reading-cdecl.html))

~~~
paulnechifor
The "spiral rule" doesn't work all the time. See this previous HN comment by
stephencanon:
[https://news.ycombinator.com/item?id=12775862](https://news.ycombinator.com/item?id=12775862)
.

Also this comment by Linus Torvalds (copy pasted because I don't know how to
link to Google+ comments):

> I don't think that works. It breaks trivially for consecutive [] or * cases,
> something that he carefully didn't have in his examples.

> So the examples were made up to make it look like it's a spiral, but type
> parsing is about precedence, not about spirals. It so happens that the
> higher-precedence operators ([] and ()) are on the right-hand side, which is
> why it "works" to start on the right.

> And it doesn't explain why

> typedef int _(_ _fn_t[][2])(void);

> is ok, but

> typedef int _( __fn_t[2][])(void);

> is not.

> "Spirals"? I don't think so.

~~~
dnquark
All these years I assumed that the 'spiral rule' and 'right-left rule' (linked
from the stephencanon comment above, and also described in my second link) are
two ways to describe the same algorithm, but, reading closely, they aren't! I
guess 'spiral rule' is a stickier name, which is why it's something that
people remember even though it's janky.

------
cestith
For anyone unable or unwilling to access that domain name for work purposes or
filtering purposes, the linked page lists this alternative:
[http://goshdarnfunctionpointers.com/](http://goshdarnfunctionpointers.com/)

~~~
jerryr
Thanks! Unfortunately, the page currently uses Hover's "stealth redirect"
which embeds the profane URL in an iframe. So, if the profane URL is actually
blocked by content filtering, you probably still won't be able to access it.

I'm actively working on the page. Once it stabilizes, I'll consider mirroring
a sanitized version instead of using the "stealth redirect".

~~~
rand77763
Wait, what type of fucked up world do people exist in where a website is
blocked due to the word "fuck".

~~~
xienze
You don't think perhaps a filter might assume that a website whose URL
contains "fuck" might have something to do with porn?

------
userbinator
The easiest and best way to learn the syntax is to not memorise specific cases
but the grammar itself, which IMHO is no more difficult than the existing
concept of operator precedence. Everyone using C should hopefully already know
that multiplication has higher precedence than addition, so likewise function
call (and array subscripting) has higher precedence than pointer dereference.
Thus this table should make it clear that combining the two operators creates
pointer-to-function:

    
    
        T x;                      T *y;
        T f();                    T (*g)();
    
        T                         pointer to T
        function returning T      pointer to function returning T
    

and the alternative, T _h(); , is parsed as T_ (h()); and thus becomes
"function returning pointer to T".

The apparent struggle I see with this syntax has always somewhat puzzled me,
because I don't see the same level of complaints about e.g. arithmetic
expressions (like 6+3*4/(2+1)) which are parsed with precedence in much the
same way. K&R even has a section on writing a parser that recognises this
syntax, so I suspect it's really not that hard, but the perception spread by
those who didn't learn the syntax but only memorised the "easy cases" is
making it appear more difficult than it really is.

~~~
hzhou321
What we need realize is that simple grammar does not always lead to simple
comprehension. Nesting the grammar elements more than a few levels is always
difficult for our current biology equipment.

------
int_19h
Every time I have to deal with the declarator syntax in C or C++, I can't help
but ponder what K&R were thinking when they designed this. It's not like there
weren't other languages back then with a saner approach.

It looks like what they did was take the syntax from B:

    
    
        auto x[10];
    

and generalize it such that the type name ended up before the variable name,
as in Algol. But in B this worked much better, because it didn't have array
_types_ (or pointer types, or function types) - everything was a machine word.
So [] in a variable declaration was just to allocate memory to which the
variable would refer; the variable itself would still be a word. When they
made [] part of the type, and added pointers and function types, the result
was a mess.

~~~
Animats
What they were thinking can be see in K&R C - there is no "typedef" in early
C. Without typedef, the syntax of C is context-independent and LALR-1. You
don't have to know if a name is a type to parse the syntax. Then came
"typedef", which broke parsing. C parsing became context-dependent. To parse C
with "typedef", and especially C++, you must read all the header files first.

With name-first declaration syntax (Pascal, Modula, Go, Rust), parsing is
context-independent again. Readability improves. Error messages improve.
Syntax-coloring editors with a single-file view don't get lost.

~~~
swhipple
That's interesting -- I was wondering in which cases typedef changes the parse
tree, and came across a few [1]:

    
    
        a (b);      /* function call or declaration */
        a * b;      /* multiplication or declaration */
        f((a) * b); /* multiplication or deref and cast */
    

> With one further change, namely deleting the production typedef-name:
> identifier and making typedef-name a terminal symbol, this grammar is
> acceptable to the YACC parser-generator.

[1] [http://eli.thegreenplace.net/2007/11/24/the-context-
sensitiv...](http://eli.thegreenplace.net/2007/11/24/the-context-sensitivity-
of-cs-grammar)

~~~
int_19h
С++ takes it all the way to 11 with templates. Here's a program that is parsed
differently depending on whether pointers are 32-bit or 64-bit:

    
    
        template<size_t N = sizeof(void*)> struct a;
    
        template<> struct a<4> {
            enum { b };
        };
    
        template<> struct a<8> {
            template<int> struct b {};
        };
    
        enum { c, d };
    
        int main() {
            a<>::b<c>d;
            d;
        }
    

Depending on which instantiation is used, the first line of main is either a
variable declaration, or two operators < and > applied in sequence.

This is especially fun to deal with for C++ IDEs that support semantic
highlighting (i.e. typenames are in a different color etc). If I remember
correctly, the first one that could handle this right was VS 2012 - it only
took 14 years after ISO C++ standard was released...

------
favorited
See related:

[http://fuckingblocksyntax.com](http://fuckingblocksyntax.com)

[http://fuckingclangwarnings.com](http://fuckingclangwarnings.com)

------
theophrastus
Or if one doesn't have cdecl installed there's an online version[1] which has
proven as a useful check on several occasions

[1] [http://cdecl.org/](http://cdecl.org/)

~~~
TorKlingberg
cdecl is nice and all, but it will fail if there is any type that isn't a
simple built-in C type. It's rare that I can just copy-paste a declaration
into cdecl.

------
bstamour
This is one of those cases where I prefer C++

    
    
        template <typename Func> using function_ptr = add_pointer_t<Func>;
    

and now declarations are a bit more sane:

    
    
        void foo(function_ptr<void (int)> callback);

~~~
Const-me
In C++, pointers to member functions are even more cumbersome than C function
pointers.

~~~
kllrnohj

      auto greet = std::mem_fn(&Foo::display_greeting);
    

Looks pretty simple to me, much simpler than C function pointers. :)

Pairs nicely with std::bind, too, like so:

    
    
      Foo foo;
      std::function<void(int)> setter = std::bind(&Foo::setValue, &foo, std::placeholders::_1);
      setter(42);

~~~
Const-me
If you need to keep that pointer in a collection, or in a class member, or
pass as a function argument, “auto” won’t work.

And std::function introduce some performance overhead.

~~~
kllrnohj
> If you need to keep that pointer in a collection, or in a class member, or
> pass as a function argument, “auto” won’t work.

Trivially solved with decltype, no need to remember anything.

decltype(std::mem_fn(&Foo::Whatever))

Typedef it if you want.

> And std::function introduce some performance overhead.

It's the same performance as a virtual function call. Pretty much the same as
a function pointer invoke.

------
hzhou321
I never got used to having variables sandwiched inside a type. I know I am not
supposed to suggest out-of-the-box, but why can't we add a new syntax, e.g.:

    
    
        return_type Fn(parameters) var;
        typedef return_type Fn(parameters) TypeName;
    

where Fn is a new keyword -- or not, if compiler understands dummy syntax --
(I would suggest &lambda; when using greek letters in code become norm).

It simplifies the C syntax a lot IMHO.

PS: now I am out-of-the-box, maybe this is better:

    
    
        Fn{return_type, param1, param2} *var;

~~~
eridius
You'd have to call it something like `_Fn` instead, or you're going to
conflict with existing code (the C standard reserves identifiers starting with
an underscore followed by a capital letter; it also reserves identifiers
starting with two underscores, but it's fairly common for code to ignore that
and use identifiers like that anyway).

------
kruhft
One of the only reasons I had "The C Programming Language" on my desk when I
was a C coder. The only thing I could never remember...

------
shmerl
The syntax is atrocious, but there isn't much C can do about it.

------
cmrdporcupine
Needs more profanity. The whole syntax is profane.

------
porjo
noscript shows a nasty looking XSS warning when I click any of the 'example
code' links.

