Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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


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


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.


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.