39

Consider the following typedefs :

typedef int (*f1)(float); typedef f1 (*f2)(double); typedef f2 (*f3)(int); 

f2 is a function that returns a function pointer. The same with f3, but the type of the function, the pointer to which f3 returns, is f2. How can I define f3 without the typedefs? I know typedefs are the cleaner and easier to understand way to define f3. However, my intention here is to understand C syntax better.

6
  • 3
    C or C++? Because C++ has some easier ways to express such things. Commented May 25, 2012 at 17:27
  • 7
    cdecl is your friend Commented May 25, 2012 at 17:43
  • 1
    cdecl.org only translates from c-syntax to English. It's not very useful for going the other way, since you have to word your declaration perfectly in the form that it expects, and that's not how English or any other natural language works. Commented May 25, 2012 at 17:48
  • 1
    Agreed. I use a specific strategy to help with that. I start with a simple declaration that I know is valid. explain then creates an English-language form for that declaration. I copy-paste and carefully extend that English-language form, add the verb declare, and, voila, I've got the answer I seek. Commented May 25, 2012 at 18:20
  • 1
    And for extra credit make f3 __stdcall, f2 __cdecl and f1 __fastcall Commented Jun 14, 2014 at 12:38

7 Answers 7

145

Start with your declaration for f1:

int (*f1)(float); 

You want f2 to be a pointer to a function returning f1, so substitute f1 in the declaration above with the declaration for f2:

int (* f1 )(float); | +-----+-----+ | | v v int (*(*f2)(double))(float); 

The declaration reads as

 f2 -- f2 *f2 -- is a pointer (*f2)( ) -- to a function (*f2)(double) -- taking a double parameter *(*f2)(double) -- returning a pointer (*(*f2)(double))( ) -- to a function (*(*f2)(double))(float) -- taking a float parameter int (*(*f2)(double))(float) -- returning int 

You repeat the process for f3:

int (*(* f2 )(double))(float); | +---+----+ | | v v int (*(*(*f3)(int))(double))(float); 

which reads as

 f3 -- f3 *f3 -- is a pointer (*f3)( ) -- to a function (*f3)(int) -- taking an int parameter *(*f3)(int) -- returning a pointer (*(*f3)(int))( ) -- to a function (*(*f3)(int))(double) -- taking a double parameter *(*(*f3)(int))(double) -- returning a pointer (*(*(*f3)(int))(double))( ) -- to a function (*(*(*f3)(int))(double))(float) -- taking a float parameter int (*(*(*f3)(int))(double))(float); -- returning int 
Sign up to request clarification or add additional context in comments.

7 Comments

+1, as this really answers the original question, which was about the correct syntax in C...
This answer should be mass-printed and distributed in computer engineering schools, it's the simplest and best way to understand function pointers declaration syntax, thank you John Bode !
Thanks, John! I was wondering where you have learned that? Is there some good book which explains it?
@Tim: It's mainly a matter of understanding declaration syntax, which unfortunately is not explained very well in most references. It just kind of "clicked" one day, and suddenly hairy declarations made sense.
@rplgn: This falls out of the "declaration mimics use" paradigm. The structure of a declarator is the same as the structure of an expression of the same type in the code. For example, if you have an array of pointers to int and you want to get the value of the i'th element, you index into the array and deference the result - x = *a[i];. The type of the expression *a[i] is int, so the declaration of a is written int *a[N];. The array-ness and pointer-ness are functions of the declarator, not the type specifier.
|
15

In C++, the miracle of templates can make this a tad easier.

#include <type_traits> std::add_pointer< std::add_pointer< std::add_pointer< int(float) >::type(double) >::type(int) >::type wow; 

3 Comments

The question was about C syntax, even if the post was tagged with C++, so it's not relevant IMHO... But nice answer by the way. : )
This is perhaps cleaner than not using any aids whatsoever (neither typedef or add_pointer), but the typedef approach is still cleaner (IMHO).
@Macmade: You tag C++, you get a C++ answer.
7

The same as with the typedef, only you place your function definition in place of its name.

Here's how f2 would look like:

typedef int (*(*f2)(double))(float); 

You can do f3 as an exercise, since I'm assuming this is homework ;)

6 Comments

The answer you give is readily available using a google search. However, the syntax for more than one level (function returning function pointer returning function pointer) is not very easy to find. And the homework reference was unwarranted.
It's exactly the same, replace f2 in my form with your f3 typedef. Not sure what you mean by Google search, it's basic C syntax..
search for "function returning function pointer" and you will find the answer you just posted. However, search for "function returning function pointer returning function pointer" and you wouldn't find the answer you are looking for.
@keveman: He gave you a very simple rule to follow that should allow you to construct any form you want. Why are you complaining that google also gives you part of the answer?
@keveman- That definition doesn't match your original description. Enter it into cdecl.org and you'll get a detailed description. I think your function arguments need to be in the order int->double->float.
|
6

Just don't. It can be done, but it will be very confusing. Typedef's are there to ease writing and reading this short of code.

A function f that takes no arguments and returns a function pointer int (*)(float) would probably be something like (untested):

int (*f())(float); 

Then for the rest you just need to keep adding parenthesis until it looks like lisp.

1 Comment

+1: Just don't. The lispish appearance of John Bode's answer is `nuff said.
6

Learn the the right-left rule:

The "right-left" rule is a completely regular rule for deciphering C declarations. It can also be useful in creating them.

Comments

2

Inspired by John Bode's answer, I want to present it in a graphical way. It should help you to understand how the compiler would lex it into an AST:

Function in function return value, recursively

We first (1) start with the packed type f3 as denoted by "typedef f2 (*f3)(int);". It is a function type by itself. Then it is unpacked one step further in (2) which puts the enclosing curly brackets, in essence performing the "is-function" CFG production of the C language (I assume that the reader has a basic idea of programming language lexical analysis). By taking a look at (3) you see that we have recursively performed the "is-function" lexing step three times, the three times being visible by the "function" nodes in the graph.

Trying my hands at the required (but simplified) CFG productions in custom notation, they could look like...

declaration -> underlying_type:specifier sp ( func-ptr-decl | specifier ) func-ptr-decl -> '(' sp '*' sp ( func-ptr-decl | specifier ) sp ')' sp '(' sp param-list sp ')' 

specifier being a string of characters that is best explained as variable names in the C programming language, sp being an optional string of whitespace, param-list being simplified as a (possibly empty) comma-separated list of declarations.

In C each statement that introduces a variable or parameter is called a declaration. Declarations consist of a location/name, the type of the data and an initializer. In the given question we have declarations whose types are pointer-to-function. The pointer-to-function type is recursively nested up to three times. The graph shows it in the way how the arrows are pointing at types meaning they are nesting inside of other types.

5 Comments

Welcome to Stack Overflow. Please read How to Answer. Images can be used to enhance answers, but answers should be accessible (useful without the images).
@Chris Hello, I am sorry that my insights have not been explained in too much detail. The answer is heavily based on internal research. I do appreciate your constructive criticism but I don't see how you not understanding the picture does warrant that everybody does not find it accessible. It could be more constructive to me if you asked a question about the picture to help me improve the answer. Otherwise I am asking myself how this StackOverflow community does value creative contributions to the complicated debate of scientific software development.
It's not that I don't understand the picture, it's that images are not accessible. They also can't be indexed by search engines, and a whole bunch of other things. Answers should always be useful even without images. Again, please read How to Answer.
@Cris First off, thank you for helping me straighten out my answer to this important question. Your time and effort are what make this community great. I want to inform you that I have read the article that you have provided to me. Curiously enough there is no mention of search engines or images being unaccessible. Thus I can solely rely on your guidance and it was an inevitable awkward situation not explained to the possible newcomer (which I really am not, but it may seem true in your statistical perspective).
You're right, that link doesn't mention images. You might find this meta post about posting images of code more relevant. Yes, that post is about code, but all of the same arguments apply here.
1

Use std::function:

typedef std::function<int(float)> f1; typedef std::function<f1(double)> f2; typedef std::function<f2(int)> f3; 

or

typedef std::function<std::function<std::function<int(float)>(double)>(int)> f3; 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.