Complex C declaration
C++CFunction PointersDeclarationC++ Problem Overview
I was just going through some code on the Internet and found this:
float * (*(*foo())[SIZE][SIZE])()
How do I read this declaration? Is there a specific set of rules for reading such complex declarations?
C++ Solutions
Solution 1 - C++
I haven't done this in a while!
Start with foo
and go right.
float * (*(*
foo()
)[SIZE][SIZE])()
> foo is a function with no arguments...
Can't go right since there's a closing parenthesis. Go left:
float * (*(
* foo()
)[SIZE][SIZE])()
> foo is a function with no arguments returning a pointer
Can't go left further, so let's cross the parentheses and go right again
float * (*
(* foo())
[SIZE][SIZE])()
float * (*
(* foo())[SIZE]
[SIZE])()
float * (*
(* foo())[SIZE][SIZE]
)()
> foo is a function with no arguments returning a pointer to an array of SIZE arrays of SIZE ...
Closing parenthesis reached, left again to reach a pointer symbol:
float * (
*(* foo())[SIZE][SIZE]
)()
> foo is a function with no arguments returning a pointer to an array of SIZE arrays of SIZE pointers to ...
Left parenthesis again, so we cross it and go right again:
float *
( *(* foo())[SIZE][SIZE])
()
float *
( *(* foo())[SIZE][SIZE])()
> foo is a function with no arguments returning a pointer to an array of SIZE arrays of SIZE pointers to a function with no arguments...
And left to the end
float * ( *(* foo())[SIZE][SIZE])()
> foo is a function with no arguments returning a pointer to an array of SIZE arrays of SIZE pointers to a function with no arguments returning a pointer to float
And whoever wrote that, please teach him to use typedef
:
// Function that returns a pointer to float
typedef float* PFloatFunc ();
// Array of pointers to PFloatFunc functions
typedef PFloatFunc* PFloatFuncArray2D[SIZE][SIZE];
// Function that returns a pointer to a PFloatFuncArray2D
PFloatFuncArray2D* foo();
Solution 2 - C++
Standard rule: find the leftmost identifier and work your way out, remembering that []
and ()
bind before *
:
foo -- foo
foo() -- is a function
*foo() -- returning a pointer
(*foo())[SIZE] -- to a SIZE-element array
(*foo())[SIZE][SIZE] -- of SIZE-element arrays
*(*foo())[SIZE][SIZE] -- of pointers
(*(*foo())[SIZE][SIZE])() -- to functions
* (*(*foo())[SIZE][SIZE])() -- returning pointers
float * (*(*foo())[SIZE][SIZE])(); -- to float
So imagine you have a bunch of functions returning pointers to float
:
float *quux();
float *bar();
float *bletch();
float *blurga();
Let's say you want to store them in a 2x2 table:
float *(*tab[SIZE][SIZE])() = {quux, bar, bletch, blurga};
tab
is a SIZE x SIZE array of pointers to functions returning pointers to float
.
Now let's decide we want a function to return a pointer to that table:
float *(*(*foo())[SIZE][SIZE])()
{
static float *(*tab[SIZE][SIZE])() = {quux, bar, bletch, blurga};
return &tab;
}
Note that you could have several functions that build tables of different functions, or organize the same functions differently:
float *(*(*qwerbl())[SIZE][SIZE])()
{
static float *(*tab[SIZE][SIZE])() = {blurga, bletch, bar, quux};
return tab;
}
which is the only reason I can think of to do something like this. You shouldn't see types like this in the wild very often (although they do crop up occasionally, and I've been guilty of writing something similarly heinous).
Solution 3 - C++
According to cdecl.org
> declare foo as function returning pointer to array SIZE of array SIZE > of pointer to function returning pointer to float
Use the spiral rule given by Luchian Grigore if you want to decode it by hand.
Solution 4 - C++
The best thing to do here is convert to a series of typedefs.
typedef float * fnReturningPointerToFloat();
typedef fnReturningPointerToFloat* fnArray[SIZE][SIZE];
fnArray* foo();
Solution 5 - C++
Generally, you could try cdecl.org but you'd need to substitute for SIZE
Say you swap SIZE
for 12, you'd get:
> declare foo as function returning pointer to array 12 of array 12 of > pointer to function returning pointer to float
I'm not sure that really helps you!
Two observations here:
- I'm guessing that this code didn't have a comment beside it explaining what the purpose of it was (i.e. not the technical explanation of what it is but what it is achieving from a functional / business perspective) If a programmer needs to use something as complex as this, they should be good enough to explain to future maintainers what purpose it serves.
- Certainly in C++ there are more obvious and probably safer ways of achieving the same thing.
Solution 6 - C++
This document gaves me the best clue about how to easily ready any C declaration :
http://c-faq.com/decl/spiral.anderson.html
> There are three simple steps to follow:
>
> - 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 ...
>
> - Keep doing this in a spiral/clockwise direction until all tokens have been covered.
>
> - Always resolve anything in parenthesis first!
Example :
+-------+
| +-+ |
| ^ | |
char *str[10];
^ ^ | |
| +---+ |
+-----------+
Question we ask ourselves: What is str?
``str is an...
- We move in a spiral clockwise direction starting with `str' and the first character we see is a `[' so, that means we have an array, so...
``str is an array 10 of...
- Continue in a spiral clockwise direction, and the next thing we encounter is the `*' so, that means we have pointers, so...
``str is an array 10 of pointers to...
- Continue in a spiral direction and we see the end of the line (the `;'), so keep going and we get to the type `char', so...
``str is an array 10 of pointers to char''
We have now ``visited'' every token; therefore we are done!
Solution 7 - C++
Although most of the answers above are good enough, there is a lack of complete set of rules for decoding complex C declarations. I have provided a complete set below to decode any complex C declaration. This set of rules is actually based on the precedence of operators. Rules such as right hand spiral rules can be thought of as a shortcut for these set of rules.
Before anything else we need to know a few things to decode the declaration.
'Basic Type' of a declaration
A C declaration always has only one basic declaration type. This is at the left most position of a declaration. For example -
int a
- basic type is 'int'float *p
- basic type is 'float'char (*p)[3]
- basic type is 'char'
Precedence and associativity
Next we need to know the precedence order of ()
, []
, and *
- dereference operator
()
,[]
- Associativity is left to right*
- Associativity is right to left
Phrase corresponding to each of the operator above
Next we need to know the decoded phrase corresponding to each operator. Examples ahead will make this point clear.
()
- function returning[SIZE]
- array of SIZE*
- pointer to
Now follow the rules below to decode the declaration
-
Always write the variable name first followed by an 'is'.
For example -
int a
- a is ...float *p
- p is ...char (*p)[3]
- p is ...
-
Always end with basic type
For example -
int a
- a is ... intfloat *p
- p is ... floatchar (*p)[3]
- p is ... char
-
Now fill the part in between using the following sub-steps
-
Starting from the name, follow the operator precedence and associativity to choose next highest priority operator and append the phrase corresponding to it to the middle part of the decoded string.
-
Repeat the sub step above for the remaining declaration until the decoding process is complete
-
NOTE 1: For simplicity, I have ignored the arguments of the function however it can be included just after the phrase corresponding to ()
.
NOTE 2: Parenthesis(()
) change the priority order of operators, just like in any arithmetic expression.
NOTE 3: You can use parenthesis in the decoded declaration to increase readability( I have done it in some examples below). Think of each set of () as a single unit.
NOTE 4: A n dimensional array is actually an array of array of ... (n-1 times) array. For ex - int A[2][3] - A is array of 2 (array of 3 int) i.e A is an array of 2 elements in which each element is an array containing 3 integers
Examples
int a
- a is intfloat *p
- p is pointer to floatchar (*p)[3]
- p is pointer to array of 3 char
Some complex declaration examples
int **p[10]
- p is array of 10 pointer to pointer to intint (*p)[10]
- p is pointer to array of 10 intint *p(char *a)
- p is function returning pointer to intint (*p(char*a))[10]
- p is function returning (pointer to (array of 10 int))int *(*p)()
- p is pointer to (function returning (pointer to int))int (*p()[20])[10]
- p is function returning (array of 20 (pointer to (array of 10 int)))
This set of rules can be used with const
as well - const qualifier modifies the term to the left of it (if present) otherwise it modifies the term to the right of it.
const int *p[10]
- p is array of 10 pointer to int constint const *p[10]
- p is array of 10 pointer to const int (this is same as 7th example)int *const p[10]
- p is array of 10 const pointer to int
Now a really complex example which will not find its use anywhere in practice but nevertheless can be used to demonstrate the decoding process
char *(*(**foo[][8])())[]
- foo is array of (array of 8 (pointer to (pointer to (function returning (pointer to (array of (pointer to char)))))))
Now at last decoding for the declaration given in the question
float * (*(*foo())[SIZE][SIZE])()
- foo is function returning (pointer to (array of SIZE (array of SIZE (pointer to (function returning pointer to float)))))
The following is the link for the article from which I read this decoding process
Example 10 has been taken from this article
Solution 8 - C++
from http://cdecl.org/
declare foo as function returning pointer to array SIZE of array SIZE of pointer to function returning pointer to float