202

I just saw a picture today and think I'd appreciate explanations. So here is the picture:

some c code

Transcription: "C isn't that hard: void (*(*f[])())() defines f as an array of unspecified size, of pointers to functions that return pointers to functions that return void."

I found this confusing and wondered if such code is ever practical. I googled the picture and found another picture in this reddit entry, and here is that picture:

some interesting explanation

Transcription: "So the symbols can be read: f [] * () * () void. f is an array of pointers that take no argument and return a pointer that takes no argument and returns void".

So this "reading spirally" is something valid? Is this how C compilers parse?
It'd be great if there are simpler explanations for this weird code.
Apart from all, can this kind of code be useful? If so, where and when?

There is a question about "spiral rule", but I'm not just asking about how it's applied or how expressions are read with that rule. I'm questioning usage of such expressions and spiral rule's validity as well. Regarding these, some nice answers are already posted.

starball
  • 20,030
  • 7
  • 43
  • 238
Motun
  • 2,149
  • 3
  • 16
  • 23
  • The syntax for declarations is intended to be equivalent to the syntax for expressions. You can just look up "operator precedence in C". Or read the standard. – EOF Dec 31 '15 at 16:06
  • 9
    How could the explanation be simpler? It covers all aspects of the definition of f w/ a couple of words for each key point. – Scott Hunter Dec 31 '15 at 16:07
  • 32
    Maybe C is hard? That actually declares `f` as an array of pointers to functions that could take any argument .. if it were `void (*(*f[])(void))(void);`, then yes, it would be functions that take no arguments ... – txtechhelp Dec 31 '15 at 16:13
  • @ScottHunter You have a point, it sounds pretty clear but somehow still confuses me. Maybe it's because I can't think of any usage of that code. – Motun Dec 31 '15 at 16:13
  • 19
    In practice, don't code such obscure code. [Use typedef for signatures](http://stackoverflow.com/a/9143434/841108) – Basile Starynkevitch Dec 31 '15 at 18:21
  • 4
    any declaration involving function pointers can be hard. That doesn't mean that normal C or C++ is hard in that same way. Other languages solve this in different ways including not having function pointers, which can be a significant omission in some cases – Kate Gregory Dec 31 '15 at 18:22
  • One has to admit... Compilers are deterministic state machines unambiguously producing code, the only part that gets confused is the human reading C. Happy new year. –  Dec 31 '15 at 19:41
  • 24
    If you squint your eyes, it looks like LISP. – user2023861 Dec 31 '15 at 19:47
  • 1
    If all else fails, there is always http://cdecl.org/ ... – John Hascall Dec 31 '15 at 21:00
  • 2
    Read [my question](http://programmers.stackexchange.com/q/117024) and this will become crystal clear. – user541686 Jan 01 '16 at 08:46
  • 1
    @Mehrdad That's one of those great questions that raises a lot of other questions and pushes you to keep learning. Thanks for the reference! – Motun Jan 01 '16 at 09:00
  • 2
    @txtechhelp Isn't that ages old interpretation deprecated yet? – AnArrayOfFunctions Jan 01 '16 at 16:52
  • 1
    @FISOCPP, well it's actually a language construct (not interpretation). `void test();` declares a function that takes an unspecified (not variadic) number of arguments. The C11 standard says it's an `obsolescent feature`, which means that the compiler implementation can still accept that code but your program won't be a `strictly conforming C program`. So, code like this: `int test(); int main() { int i; i = test(); return i; } int test(int x, int y, int z) { return x*y*z; }` will compile without error (and no warnings), it just won't be strictly conforming, and give fun results back :) – txtechhelp Jan 01 '16 at 20:55
  • 2
    Possible duplicate of [What's the meaning of this piece of code? void (\*signal(int sig, void (\*func)(int)))(int);](http://stackoverflow.com/questions/3706704/whats-the-meaning-of-this-piece-of-code-void-signalint-sig-void-funcin) – Peter Mortensen Jan 01 '16 at 21:30
  • 2
    Read also [this answer](http://stackoverflow.com/a/30345939/4265352). – axiac Jan 02 '16 at 00:11
  • 3
    Possible duplicate of [What does \`((void (\*)())0x1000)();\` mean?](http://stackoverflow.com/questions/30345445/what-does-void-0x1000-mean) – axiac Jan 02 '16 at 00:12
  • 1
    All languages have their quirks. While the Clockwise/Spiral rule of C is really weird (until you "understand" it), it's interesting to note that the designers of Go, while _thoroughly rejecting_ this often unreadable and error-prone and confusing and needlessly complex (okay I'm kidding, sorta) style of declaration, also called it ["an unusual and clever approach"](http://blog.golang.org/gos-declaration-syntax). – Ray Toal Jan 03 '16 at 05:17
  • Out of curiosity, where did you find this? – user4520 Jan 03 '16 at 11:06
  • @szczurcio A friend posted it on facebook, I don't know where he got this from. – Motun Jan 03 '16 at 11:39
  • holy crap... i've been thinking i'm quite good at C ( at least my grades in university told me so ). But this is insane ;) – shock_gone_wild Jan 03 '16 at 15:02

13 Answers13

120

There is a rule called the "Clockwise/Spiral Rule" to help find the meaning of a complex declaration.

From c-faq:

There are three simple steps to follow:

  1. Starting with the unknown element, move in a spiral/clockwise direction; when ecountering the following elements replace them with the corresponding english statements:

    [X] or []
    => Array X size of... or Array undefined size of...

    (type1, type2)
    => function passing type1 and type2 returning...

    *
    => pointer(s) to...

  2. Keep doing this in a spiral/clockwise direction until all tokens have been covered.

  3. Always resolve anything in parenthesis first!

You can check the link above for examples.

Also note that to help you there is also a website called:

http://www.cdecl.org

You can enter a C declaration and it will give its english meaning. For

void (*(*f[])())()

it outputs:

declare f as array of pointer to function returning pointer to function returning void

EDIT:

As pointed out in the comments by Random832, the spiral rule does not address array of arrays and will lead to a wrong result in (most of) those declarations. For example for int **x[1][2]; the spiral rule ignores the fact that [] has higher precedence over *.

When in front of array of arrays, one can first add explicit parentheses before applying the spiral rule. For example: int **x[1][2]; is the same as int **(x[1][2]); (also valid C) due to precedence and the spiral rule then correctly reads it as "x is an array 1 of array 2 of pointer to pointer to int" which is the correct english declaration.

Note that this issue has also been covered in this answer by James Kanze (pointed out by haccks in the comments).

Community
  • 1
  • 1
ouah
  • 142,963
  • 15
  • 272
  • 331
  • 5
    I wish cdecl.org was better – Grady Player Dec 31 '15 at 22:41
  • 11
    There is no "spiral rule"... "int ***foo[][][]" defines an array of arrays of arrays of pointers to pointers to pointers. The "spiral" only comes from the fact that this declaration happened to group things in parentheses in a way that caused them to alternate. It's everything on the right, then left, within each set of parentheses. – Random832 Jan 01 '16 at 01:43
  • 1
    @Random832 There is a "spiral rule", and it covers the case you just mentioned, i.e. talks about how to deal with parenthesis/arrays etc. Of course is not a Standard C rule, but a good mnemonic for figuring out how to deal with complicated declarations. IMHO, it is extremely useful and saves you when in trouble or when [cdecl.org](http://cdecl.org/) cannot parse the declaration. Of course one shouldn't abuse such declarations, but it's good to know how they are parsed. – vsoftco Jan 01 '16 at 01:55
  • 5
    @vsoftco But it's not "moving in a spiral/clockwise direction" if you only turn around when you reach the parentheses. – Random832 Jan 01 '16 at 03:01
  • 2
    ouah, you should mention that [spiral rule is not universal](http://stackoverflow.com/a/16265389/2455888). – haccks Jan 01 '16 at 20:56
  • This spiral rule us just a shortcut that tries to gloss over order of precedence. You could say that it works "most of the time", but that's not really well-defined. – Jeff Wolski Jan 05 '16 at 19:48
  • 1
    Not a rule but a technique. – g24l Jan 06 '16 at 00:54
  • It seems like the group of three square brackets defining the three-dimensional array is one node along the spiral path, and then the next node is the group of pointer-to-pointer-to-pointer operators. So, the spiral rule seems to hold in this case, too. BTW, it doesn't have to be clockwise. Everywhere you go clockwise you get the same result if you go counter-clockwise, too. – Erik Midtskogen Jul 29 '23 at 14:25
117

The "spiral" rule kind of falls out of the following precedence rules:

T *a[]    -- a is an array of pointer to T
T (*a)[]  -- a is a pointer to an array of T
T *f()    -- f is a function returning a pointer to T
T (*f)()  -- f is a pointer to a function returning T

The subscript [] and function call () operators have higher precedence than unary *, so *f() is parsed as *(f()) and *a[] is parsed as *(a[]).

So if you want a pointer to an array or a pointer to a function, then you need to explicitly group the * with the identifier, as in (*a)[] or (*f)().

Then you realize that a and f can be more complicated expressions than just identifiers; in T (*a)[N], a could be a simple identifier, or it could be a function call like (*f())[N] (a -> f()), or it could be an array like (*p[M])[N], (a -> p[M]), or it could be an array of pointers to functions like (*(*p[M])())[N] (a -> (*p[M])()), etc.

It would be nice if the indirection operator * was postfix instead of unary, which would make declarations somewhat easier to read from left to right (void f[]*()*(); definitely flows better than void (*(*f[])())()), but it's not.

When you come across a hairy declaration like that, start by finding the leftmost identifier and apply the precedence rules above, recursively applying them to any function parameters:

         f              -- f
         f[]            -- is an array
        *f[]            -- of pointers  ([] has higher precedence than *)
       (*f[])()         -- to functions
      *(*f[])()         -- returning pointers
     (*(*f[])())()      -- to functions
void (*(*f[])())();     -- returning void

The signal function in the standard library is probably the type specimen for this kind of insanity:

       signal                                       -- signal
       signal(                          )           -- is a function with parameters
       signal(    sig,                  )           --    sig
       signal(int sig,                  )           --    which is an int and
       signal(int sig,        func      )           --    func
       signal(int sig,       *func      )           --    which is a pointer
       signal(int sig,      (*func)(int))           --    to a function taking an int                                           
       signal(int sig, void (*func)(int))           --    returning void
      *signal(int sig, void (*func)(int))           -- returning a pointer
     (*signal(int sig, void (*func)(int)))(int)     -- to a function taking an int
void (*signal(int sig, void (*func)(int)))(int);    -- and returning void

At this point most people say "use typedefs", which is certainly an option:

typedef void outerfunc(void);
typedef outerfunc *innerfunc(void);

innerfunc *f[N];

But...

How would you use f in an expression? You know it's an array of pointers, but how do you use it to execute the correct function? You have to go over the typedefs and puzzle out the correct syntax. By contrast, the "naked" version is pretty eyestabby, but it tells you exactly how to use f in an expression (namely, (*(*f[i])())();, assuming neither function takes arguments).

John Bode
  • 119,563
  • 19
  • 122
  • 198
64

In C, declaration mirrors usage—that’s how it’s defined in the standard. The declaration:

void (*(*f[])())()

Is an assertion that the expression (*(*f[i])())() produces a result of type void. Which means:

  • f must be an array, since you can index it:

    f[i]
    
  • The elements of f must be pointers, since you can dereference them:

    *f[i]
    
  • Those pointers must be pointers to functions taking no arguments, since you can call them:

    (*f[i])()
    
  • The results of those functions must also be pointers, since you can dereference them:

    *(*f[i])()
    
  • Those pointers must also be pointers to functions taking no arguments, since you can call them:

    (*(*f[i])())()
    
  • Those function pointers must return void

The “spiral rule” is just a mnemonic that provides a different way of understanding the same thing.

Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • 5
    Great way of looking at it that I've never seen before. +1 – tbodt Jan 02 '16 at 20:53
  • 5
    Nice. Seen this way, it _really is simple_. Actually rather easier than something like `vector< function()>* > f`, especially if you add in the `std::`s. (But well, the example _is_ contrived... even `f :: [IORef (IO (IO ()))]` looks weird.) – leftaroundabout Jan 02 '16 at 23:50
  • Thanks for your answer! With the `f[i]` written this definitely makes sense to me, but I don't see why this original part of the code `(*f[])()` is valid. Isn't it a call to a function of the array without defining which element to choose? – Timo Denk Jan 05 '16 at 21:19
  • 1
    @TimoDenk: The declaration `a[x]` indicates that the expression `a[i]` is valid when `i >= 0 && i < x`. Whereas, `a[]` leaves the size unspecified, and is therefore identical to `*a`: it indicates that the expression `a[i]` (or equivalently `*(a + i)`) is valid for *some* range of `i`. – Jon Purdy Jan 05 '16 at 21:37
  • 4
    This is by far the easiest way to think about C types, thanks for this – Alex Ozer Jan 05 '16 at 22:22
  • 4
    I love this! Much easier to reason about than silly spirals. `(*f[])()` is a type you can index, then dereference, then call, so it's an array of pointers to functions. – Lynn Jan 16 '16 at 00:46
42

So this "reading spirally" is something valid?

Applying spiral rule or using cdecl are not valid always. Both fails in some cases. Spiral rule works for many cases, but it is not universal.

To decipher complex declarations remember these two simple rules:

  • Always read declarations from inside out: Start from innermost, if any, parenthesis. Locate the identifier that's being declared, and start deciphering the declaration from there.

  • When there is a choice, always favour [] and () over *: If * precedes the identifier and [] follows it, the identifier represents an array, not a pointer. Likewise, if * precedes the identifier and () follows it, the identifier represents a function, not a pointer. (Parentheses can always be used to override the normal priority of [] and () over *.)

This rule actually involves zigzagging from one side of the identifier to the other.

Now deciphering a simple declaration

int *a[10];

Applying rule:

int *a[10];      "a is"  
     ^  

int *a[10];      "a is an array"  
      ^^^^ 

int *a[10];      "a is an array of pointers"
    ^

int *a[10];      "a is an array of pointers to `int`".  
^^^      

Let's decipher the complex declaration like

void ( *(*f[]) () ) ();  

by applying the above rules:

void ( *(*f[]) () ) ();        "f is"  
          ^  

void ( *(*f[]) () ) ();        "f is an array"  
           ^^ 

void ( *(*f[]) () ) ();        "f is an array of pointers" 
         ^    

void ( *(*f[]) () ) ();        "f is an array of pointers to function"   
               ^^     

void ( *(*f[]) () ) ();        "f is an array of pointers to function returning pointer"
       ^   

void ( *(*f[]) () ) ();        "f is an array of pointers to function returning pointer to function" 
                    ^^    

void ( *(*f[]) () ) ();        "f is an array of pointers to function returning pointer to function returning `void`"  
^^^^

Here is a GIF demonstrating how you go (click on image for larger view):

enter image description here


The rules mentioned here is taken from the book C Programming A Modern Approach by K.N KING.

Community
  • 1
  • 1
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 1
    This is just like the standard's approach i.e. "declaration mirrors usage". I'd like to ask something else at this point though: Do you suggest K. N. King's book? I'm seeing lots of nice reviews about the book. – Motun Jan 01 '16 at 22:54
  • 2
    Yeah. I do suggest that book. I started programming from that book. Good texts and problems in there. – haccks Jan 01 '16 at 22:59
  • Can you provide an example of cdecl failing to understand a declaration? I thought cdecl used the same parsing rules as compilers, and as far as I can tell it always works. – Fabio says Reinstate Monica Jan 04 '16 at 00:47
  • @FabioTurati; A function can't return arrays or function. `char (x())[5]` should result in syntax error but, cdecl parse it as: *declare `x` as function returning array 5 of `char`*. – haccks Jan 04 '16 at 02:16
12

It's only a "spiral" because there happens to be, in this declaration, only one operator on each side within each level of parentheses. Claiming that you proceed "in a spiral" generally would suggest you alternate between arrays and pointers in the declaration int ***foo[][][] when in reality all of the array levels come before any of the pointer levels.

Random832
  • 37,415
  • 3
  • 44
  • 63
  • Well, in the "spiral approach", you go as far right as you can, then as far left as you can, etc. But it's often explained erroneously... – Lynn Jan 16 '16 at 00:48
7

I doubt constructions like this can have any use in real life. I even detest them as interview questions for the regular developers (likely OK for compiler writers). typedefs should be used instead.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • 3
    Nonetheless, it's important to know how to parse it, even if only to know how to parse the typedef! – inetknght Dec 31 '15 at 16:21
  • 1
    @inetknght, the way you do it with typedefs is to have them simple enough so that no parsing would be required. – SergeyA Dec 31 '15 at 16:38
  • 2
    People who ask these types of questions during interviews only do it to stroke their Egos. – Casey Dec 31 '15 at 16:47
  • I have used constructs similar to this in production code; I had a lookup table of parsing functions keyed by elements of a file name and a function to search that table and return the right parsing function. The prototype was something like `void (*getParser(const char *ftype, const char *ext))(const char *fname);`. – John Bode Dec 31 '15 at 17:59
  • 1
    @JohnBode, and you would do yourself a favor by typedefing return value of the function. – SergeyA Dec 31 '15 at 18:19
  • @SergeyA: As I said in my answer, `typedef`s hide information (that's kind of their point); the "naked" form tells me *exactly* how `getParser` is to be used in an expression, and how its return value should be used as well. If this was some ADT that I just passed around in an API (like the `FILE` type), then a `typedef` is the right answer. Given that I have to actually *call* the function, it's better to have that information explcitily available in the prototype. – John Bode Dec 31 '15 at 18:34
  • 1
    @JohnBode, i find it a matter of personal choice not worth debating. I see your preference, I still have mine. – SergeyA Dec 31 '15 at 18:37
  • 1
    This should be a comment, not an answer. – Fabio says Reinstate Monica Dec 31 '15 at 20:46
  • Thank god this is an answer and we can up vote against the madness of using this type of obfuscation . @JohnBode using typedefs is the way to avoid people coming at your desk an hitting you with a hammer. Otherwise your answer above is perfect. – g24l Jan 06 '16 at 01:03
7

As a random trivia factoid, you might find it amusing to know that there's an actual word in English to describe how C declarations are read: Boustrophedonically, that is, alternating right-to-left with left-to-right.

Reference: Van der Linden, 1994 - Page 76

asamarin
  • 1,544
  • 11
  • 15
  • 1
    That word doesn't indicate *within* as in nested by parens or on a single line. It describes a "snake" pattern, with an LTR line followed by an RTL line. – Potatoswatter Jan 03 '16 at 05:29
5

Regarding the usefulness of this, when working with shellcode you see this construct a lot:

int (*ret)() = (int(*)())code;
ret();

While not quite as syntactically complicated, this particular pattern comes up a lot.

More complete example in this SO question.

So while the usefulness to the extent in the original picture is questionable (I would suggest that any production code should be drastically simplified), there are some syntactical constructs that do come up quite a bit.

Motun
  • 2,149
  • 3
  • 16
  • 23
Casey
  • 12,070
  • 18
  • 71
  • 107
5

The declaration

void (*(*f[])())()

is just an obscure way of saying

Function f[]

with

typedef void (*ResultFunction)();

typedef ResultFunction (*Function)();

In practice, more descriptive names will be needed instead of ResultFunction and Function. If possible I would also specify the parameter lists as void.

August Karlstrom
  • 10,773
  • 7
  • 38
  • 60
5

I happen to be the original author of the spiral rule that I wrote oh so many years ago (when I had a lot of hair :) and was honored when it was added to the cfaq.

I wrote the spiral rule as a way to make it easier for my students and colleagues to read the C declarations "in their head"; i.e., without having to use software tools like cdecl.org, etc. It was never my intent to declare that the spiral rule be the canonical way to parse C expressions. I am though, delighted to see that the rule has helped literally thousands of C programming students and practitioners over the years!

For the record,

It has been "correctly" identified numerous times on many sites, including by Linus Torvalds (someone whom I respect immensely), that there are situations where my spiral rule "breaks down". The most common being:

char *ar[10][10];

As pointed out by others in this thread, the rule could be updated to say that when you encounter arrays, simply consume all the indexes as if written like:

char *(ar[10][10]);

Now, following the spiral rule, I would get:

"ar is a 10x10 two-dimensional array of pointers to char"

I hope the spiral rule carries on its usefulness in learning C!

P.S.:

I love the "C isn't hard" image :)

4

Remember these rules for C declares
And precedence never will be in doubt:
Start with the suffix, proceed with the prefix,
And read both sets from the inside, out.
-- me, mid-1980's

Except as modified by parentheses, of course. And note that the syntax for declaring these exactly mirrors the syntax for using that variable to get an instance of the base class.

Seriously, this isn't hard to learn to do at a glance; you just have to be willing to spend some time practising the skill. If you're going to maintain or adapt C code written by other people, it's definitely worth investing that time. It's also a fun party trick for freaking out other programmers who haven't learned it.

For your own code: as always, the fact that something can be written as a one-liner does't mean it should be, unless it is an extremely common pattern that has become a standard idiom (such as the string-copy loop). You, and those who follow you, will be much happier if you build complex types out of layered typedefs and step-by-step dereferences rather than relying on your ability to generate and parse these "at one swell foop." Performance will be just as good, and code readability and maintainability will be tremendously better.

It could be worse, you know. There was a legal PL/I statement that started with something like:

if if if = then then then = else else else = if then ...
Shadow The GPT Wizard
  • 66,030
  • 26
  • 140
  • 208
keshlam
  • 7,931
  • 2
  • 19
  • 33
  • 2
    The PL/I statement was `IF IF = THEN THEN THEN = ELSE ELSE ELSE = ENDIF ENDIF` and is parsed as `if (IF == THEN) then (THEN = ELSE) else (ELSE = ENDIF)`. – Cole Tobin Jan 01 '16 at 09:04
  • I _think_ there was a version which took it one step farther by using a conditional IF/THEN/ ELSE expression (equivalent to C's ?:), which got the third set into the mix... but it's been a few decades and may have depended upon a particular dialect of the language. Point remains that any language has at least one pathological form. – keshlam Jan 01 '16 at 12:45
4

I found method described by Bruce Eckel to be helpful and easy to follow:

Defining a function pointer

To define a pointer to a function that has no arguments and no return value, you say:

void (*funcPtr)();

When you are looking at a complex definition like this, the best way to attack it is to start in the middle and work your way out. “Starting in the middle” means starting at the variable name, which is funcPtr. “Working your way out” means looking to the right for the nearest item (nothing in this case; the right parenthesis stops you short), then looking to the left (a pointer denoted by the asterisk), then looking to the right (an empty argument list indicating a function that takes no arguments), then looking to the left (void, which indicates the function has no return value). This right-left-right motion works with most declarations.

To review, “start in the middle” (“funcPtr is a ...”), go to the right (nothing there – you're stopped by the right parenthesis), go to the left and find the ‘*’ (“... pointer to a ...”), go to the right and find the empty argument list (“... function that takes no arguments ... ”), go to the left and find the void (“funcPtr is a pointer to a function that takes no arguments and returns void”).

You may wonder why *funcPtr requires parentheses. If you didn't use them, the compiler would see:

void *funcPtr();

You would be declaring a function (that returns a void*) rather than defining a variable. You can think of the compiler as going through the same process you do when it figures out what a declaration or definition is supposed to be. It needs those parentheses to “bump up against” so it goes back to the left and finds the ‘*’, instead of continuing to the right and finding the empty argument list.

Complicated declarations & definitions

As an aside, once you figure out how the C and C++ declaration syntax works you can create much more complicated items. For instance:

//: C03:ComplicatedDefinitions.cpp

/* 1. */     void * (*(*fp1)(int))[10];

/* 2. */     float (*(*fp2)(int,int,float))(int);

/* 3. */     typedef double (*(*(*fp3)())[10])();
             fp3 a;

/* 4. */     int (*(*f4())[10])();


int main() {} ///:~ 

Walk through each one and use the right-left guideline to figure it out. Number 1 says “fp1 is a pointer to a function that takes an integer argument and returns a pointer to an array of 10 void pointers.”

Number 2 says “fp2 is a pointer to a function that takes three arguments (int, int, and float) and returns a pointer to a function that takes an integer argument and returns a float.”

If you are creating a lot of complicated definitions, you might want to use a typedef. Number 3 shows how a typedef saves typing the complicated description every time. It says “An fp3 is a pointer to a function that takes no arguments and returns a pointer to an array of 10 pointers to functions that take no arguments and return doubles.” Then it says “a is one of these fp3 types.” typedef is generally useful for building complicated descriptions from simple ones.

Number 4 is a function declaration instead of a variable definition. It says “f4 is a function that returns a pointer to an array of 10 pointers to functions that return integers.”

You will rarely if ever need such complicated declarations and definitions as these. However, if you go through the exercise of figuring them out you will not even be mildly disturbed with the slightly complicated ones you may encounter in real life.

Taken from: Thinking in C++ Volume 1, second edition, chapter 3, section "Function Addresses" by Bruce Eckel.

user3496846
  • 1,627
  • 3
  • 16
  • 28
3
  • void (*(*f[]) ()) ()

Resolving void >>

  • (*(*f[]) ()) () = void

Resoiving () >>

  • (*(*f[]) ()) = function returning (void)

Resolving * >>

  • (*f[]) () = pointer to (function returning (void) )

Resolving () >>

  • (*f[]) = function returning (pointer to (function returning (void) ))

Resolving * >>

  • f[] = pointer to (function returning (pointer to (function returning (void) )))

Resolving [ ] >>

  • f = array of (pointer to (function returning (pointer to (function returning (void) ))))
Shubham
  • 172
  • 8