Complex C Declaration

Complex declarations

Here is a great article about how to read complex declarations in C: http://www.codeproject.com/KB/cpp/complex_declarations.aspx

It helped me a lot!

Especially - You should read "The right rule" section. Here quote:

int * (* (*fp1) (int) ) [10];
This can be interpreted as follows:

  1. Start from the variable name -------------------------- fp1
  2. Nothing to right but ) so go left to find * -------------- is a pointer
  3. Jump out of parentheses and encounter (int) --------- to a
    function that takes an int as argument
  4. Go left, find * ---------------------------------------- and returns a pointer
  5. Jump put of parentheses, go right and hit [10] -------- to an array of
    10
  6. Go left find * ----------------------------------------- pointers to
  7. Go left again, find int -------------------------------- ints.

Complex C declaration

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();

How to decipher complex pointer declarations in C?

cdecl.org is often linked to such questions. No doubt that it make easier to decipher any complex declaration, but at the same time it just provide an abstracted information. Being a C or C++ programmer one should know how to decipher complex declaration manually. Spiral Rule help to some extent but fails in some cases. This answer will help programmers to decipher any complex declaration manually.


Remember these two simple rules:

  1. Always read declaration from the inside out.
  2. When there is a choice, always favor [] and () over *.

The first rule simply states that, locate the variable that is being declared and start deciphering the declaration from it.

For second rule, if * precedes the identifier and [] or () follows it, then the identifier represents an array or function (respectively), not a pointer.

Example 1:

char *y[5]; 
  • Variable/identifier is y.
  • * precedes y and follows [].
  • y must be an array.

Combining above deciphering will result in: y is an array of 5 pointers to char.

Also note that you can always use parentheses to override the normal priority of [] or ().

Example 2:

void (*pf) (int);
  • Variable/identifier is pf.
  • *pf is enclosed in parenthesis, it must be a pointer.
  • () follows *pf, means pf must points to a function.
  • Since () encloses int, function must expects an argument of type int.

So, pf is a pointer to function that expects an int argument and returns nothing.

Now, what would you get after deciphering the following declaration

int *(*a[5])(void);  

?

Answer:


a is an array of pointers to functions that expects no argument and returning pointer to int.


Note:
Note that both of

char *y[];
char **z[];

will cause compilation error if they are not declared as arguments of a function. If they are function's argument then char *y[] is equivalent to char **y and char **z[] is equivalent to char ***z.

If that's not the case, then you need to specify the dimension as I did in my first example.

Complex pointer declarations

This declaration indeed should not be allowed; functions may not return array types, and you may not have arrays of function type:

6.7.6.2 Array declarators
Constraints
1 In addition to optional type qualifiers and the keyword static, the [ and ] may delimit
an expression or *. If they delimit an expression (which specifies the size of an array), the
expression shall have an integer type. If the expression is a constant expression, it shall
have a value greater than zero. The element type shall not be an incomplete or function
type.
The optional type qualifiers and the keyword static shall appear only in a
declaration of a function parameter with an array type, and then only in the outermost
array type derivation.

...

6.7.6.3 Function declarators (including prototypes)
Constraints
1 A function declarator shall not specify a return type that is a function type or an array
type.

C 2011 Online Draft

Emphasis added. I don't know why cdecl doesn't return some kind of error on that.

If you want a function that returns a pointer to an array, you'd write

int (*p())[N];

This parses as

      p         -- p is a
p() -- function returning
*p() -- pointer to
(*p())[N] -- array of
int (*p())[N]; -- int

If you want an array of function pointers, you'd write

int (*p[N])();

which parses as

      p        -- p is an
p[N] -- array of
*p[N] -- pointer to
(*p[N])() -- function returning
int (*p[N])(); -- int

If you want a function that returns a pointer to an array of pointers, you'd write

int *(*p())[N];

which reads as

       p         -- p is a
p() -- function returning
*p() -- pointer to
(*p())[N] -- array of
*(*p())[N] -- pointer to
int *(*p())[N]; -- int

Postfix [] and () have higher precedence than unary *, so:

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 pointer to T
T (*f)(); // f is a pointer to a function returning T

Starting with those rules, you can use substitution to build more complex declarations. If you want a function returning a pointer to an array, take

T (*a)[N];

and replace a with a function declarator:

T (*  a  )[N];
|
V
T (* f() )[N];

If you want an array of pointers to functions, take a function pointer declarator

T (*f)();

and replace f with an array declarator:

T (*   f   )()
|
V
T (* a[N] )();

From here, you should be able to read (and generate) more complex types.

cdecl's a nice tool, but once you learn how C declaration syntax really works, you shouldn't need it anymore.

Complex declaration in C- Programming language

Simply break the declaration into parts.

The inner most pair of braces : ( * f[3] ) means 'f' is an array of 3 elements which are pointers.

( *f )() means 'f' is a pointer to function, so

( * f[3] ) () means f is an array of 3 pointers to function.

( * ( * f[3] ) ()) means f is an array of 3 pointers to function returning a pointer.

Hence,

char ( * ( * f[3] ) ()) [5]  ;

means f is an array of 3 pointers to function returning a pointer to array of 5 characters

How do I read this complex declaration in C?

Start with the leftmost identifier and work your way out, remembering that [] and () bind before *, so *a[] is an array of pointers, (*a)[] is a pointer to an array, *f() is a function returning a pointer, and (*f)() is a pointer to a function:

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

signal associates a signal handler function func with a signal sig, and returns the pointer to the old signal handler function:

void new_interrupt_handler(int sig)
{
... // do something interesting with interrupt signal
}

int main(void)
{
void (*old_interrupt_handler)(int);
...
/**
* Set up our new interrupt handler
*/
old_interrupt_handler = signal(SIGINT, new_interrupt_handler);
...
/**
* Restore original interrupt handler
*/
signal(SIGINT, old_interrupt_handler);
...
}

How do I understand complicated function declarations?

As others have pointed out, cdecl is the right tool for the job.

If you want to understand that kind of declaration without help from cdecl, try reading from the inside out and right to left

Taking one random example from your list char (*(*X[3])())[5];
Start at X, which is the identifier being declared/defined (and the innermost identifier):

char (*(*X[3])())[5];
^

X is

X[3]
^^^

X is an array of 3

(*X[3])
^ /* the parenthesis group the sub-expression */

X is an array of 3 pointers to

(*X[3])()
^^

X is an array of 3 pointers to function accepting an unspecified (but fixed) number of arguments

(*(*X[3])())
^ /* more grouping parenthesis */

X is an array of 3 pointers to function accepting an unspecified (but fixed) number of arguments and returning a pointer

(*(*X[3])())[5]
^^^

X is an array of 3 pointers to function accepting an unspecified (but fixed) number of arguments and returning a pointer to an array of 5

char (*(*X[3])())[5];
^^^^ ^

X is an array of 3 pointers to function accepting an unspecified (but fixed) number of arguments and returning a pointer to an array of 5 char.

Complex type declaration with an anonymous profile as a parameter

In int (*replace_f(int (*func)(int, int)))(int, int);, the declaration of func has function prototype scope because it appears inside a function declaration. So this declaration declares two identifiers, replace_f and func, but the scope of func ends at the end of the function declaration.

That makes func useless in this case. There are situations where an identifier in function prototype scope is useful, such as void foo(int x, int y, float a[x][y]);, where x and y are used to describe the dimensions of a.

But then, the author claims if we need to declare the type pointer to the early function we have to declare as: … not as …

This is incorrect. You can declare ptr_replace in any of the three ways shown.

He claims that it's not available to use two or more identifiers in the type declaration. Why does it work then?

It does work to declare replace_f. It does not work to declare func or any_other_identifier. So perhaps the original text meant something like this?



Related Topics



Leave a reply



Submit