Spiral Rule and 'Declaration Follows Usage' for Parsing C and C++ Declarations

Spiral rule and 'declaration follows usage' for parsing C and C++ declarations

You just have to build it up in steps.

char *X();  // X =~ (*(*a[N])())

Function returning char*

char *(*Y())();  // Y =~ (*a[N])

Function returning pointer to function returning char*.

In a declaration, just as in an expression (declaration follow usage), postfix [] has a higher precedence that unary * so *a[N] is equivalent to *(a[N]), not (*a)[N].

char *(*(*Z)())();  // Z =~ a[N]

Pointer to function returning pointer to function returning char*.

char *(*(*a[N])())();

Array of N pointers to functions returning a pointer to function returning char*.

Equivalent C declarations

They are not equal. in the first case x is a pointer to an array of 10 integers, in the second case x is an array of 10 integers.

The two types are different. You can see they're not the same thing by checking sizeof in the two cases.

C isn't that hard: void ( *( *f[] ) () ) ()

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).

Parsing C declarations

First, some basic rules:

T   *a[N];   // a is an array of pointer to T
T (*a)[N]; // 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
const T *p; // p is a non-const pointer to const T
T const *p; // same as above
T * const p; // p is a const pointer to non-const T

In both declarators and expressions, the [] and () operators have higher precedence than the * operator, so you need to explicitly group it with the identifier when working with pointers to arrays ((*a)[N]) and pointers to functions ((*f)()).

When you find a hairy declaration, find the left-most identifier and work your way out, remembering the rules above, and applying them recursively to any function parameters:

       signal                               -- signal
signal( ) -- is a function taking
signal( ) -- parameter unnamed
signal(int, ) -- of type int
signal(int, fp ) -- parameter fp
signal(int, (*fp) ) -- is a pointer
signal(int, (*fp)( )) -- to a function taking
signal(int, (*fp)( )) -- parameter unnamed
signal(int, (*fp)(int)) -- of type int
signal(int, void (*fp)(int)) -- returning void
(*signal(int, void (*fp)(int))) -- returning a pointer
(*signal(int, void (*fp)(int)))( ) -- to a function taking
(*signal(int, void (*fp)(int)))( ) -- parameter unnamed
(*signal(int, void (*fp)(int)))(int) -- of type int
void (*signal(int, void (*fp)(int)))(int); -- returning void

In English, signal is a function that takes an integer and a pointer to a signal function as parameters and returns a pointer to a handling function.

Occasionally you don't have an identifier (as in a function prototype where only the types are specified), so you have to mentally put in a placeholder (call it λ) and apply the rules to that placeholder:

void (*signal(int λ, void (*fp)(int λ)))(int λ);

Get confused with this declaration - int (*(*foo)(double))[3]

You just read from the inside out remembering that postfix array ([]) and function "call" (()) bind tighter than prefix pointer (*):

      (*foo)              // foo is a pointer...
(*foo)(double) // to a function taking a double...
(*(*foo)(double)) // returning a pointer...
(*(*foo)(double))[3] // to an array of 3...
int (*(*foo)(double))[3]; // ints

(To work out where to start you might want to work from the outside in, but you need to read back from the inside out to read the declaration in the conventional order.)

A way to efficiently parse function pointer declaration syntax

The method I've developed is to start with the leftmost identifier and work out, keeping in mind the following precedence rules:

T *a[N];   // a is an array of pointer
T (*a)[N]; // a is a pointer to an array
T *f(); // f is a function returning a pointer
T (*f)(); // if is a pointer to a function

and doing that recursively for any function parameters.

I'm going to use λ to represent unnamed parameters, so we get something like this:

         somename                               -- somename is
*somename -- a pointer to
(*somename)( ) -- a function taking
(*somename)( λ ) -- unnamed parameter is
(*somename)( *λ ) -- a pointer to
(*somename)( (*λ)()) -- a function taking unspecified parameters
(*somename)(void (*λ)()) -- returning void
*(*somename)(void (*λ)()) -- returning a pointer to
(*(*somename)(void (*λ)()))( ) -- a function taking
(*(*somename)(void (*λ)()))( λ ) -- unnamed parameter is
(*(*somename)(void (*λ)()))( *λ ) -- a pointer to
(*(*somename)(void (*λ)()))( (*λ)()) -- a function taking unspecified parameters
(*(*somename)(void (*λ)()))(void (*λ)()) -- returning void
void (*(*somename)(void (*λ)()))(void (*λ)()); -- returning void

In English, somename is a pointer to a function that takes a pointer to another function as an argument and returns a pointer yet another function that takes a pointer to a still another function as its argument and returns void.

Types this obnoxious are rare in the wild, but they do pop up occasionally.



Related Topics



Leave a reply



Submit