tags:

views:

878

answers:

11

I have heard of some methods, but none of them have stuck. Personally I try to avoid complex types in C and try to break them into component typedef.

I'm now faced with maintaining some legacy code from a so called 'three star programmer', and I'm having a hard time reading some of the ***code[][].

How do you read complex C declarations?

+1  A: 

Back when I was doing C, I made use of a program called "cdecl". It appears that it's in Ubuntu Linux in the cutils or cdecl package, and it's probably available elsewhere.

Paul Tomblin
+2  A: 

One word: cdecl

Damnit, beaten by 15 seconds!

Dark Shikari
+3  A: 

Cdecl (and c++decl) is a program for encoding and decoding C (or C++) type declarations.

http://gd.tuwien.ac.at/linuxcommand.org/man_pages/cdecl1.html

smink
A: 

Common readability problems include function pointers and the fact that arrays are really pointers, and that multidimensional arrays are really single dimension arrays (which are really pointers). Hope that helps some.

In any case, whenever you do understand the declarations, maybe you can figure out a way to simplify them to make them more readable for the next guy.

Doug T.
+11  A: 

This article explains a relatively simple 7 rules which will let you read any C declaration, if you find yourself wanting or needing to do so manually: http://www.ericgiguere.com/articles/reading-c-declarations.html

If you're fine with a tool, then I second the suggestion to use the program cdecl: http://gd.tuwien.ac.at/linuxcommand.org/man_pages/cdecl1.html

Eli Courtwright
+1  A: 

cdecl offers a command line interface so let's give it a try:

cdecl> explain int ***c[][]
declare c as array of array of pointer to pointer to pointer to int

another example

 explain int (*IMP)(ID,SEL) 
declare IMP as pointer to function (ID, SEL) returning int

However there is a whole chapter about that in the book "C Deep Secrets", named "Unscrambling declarations in C.

Regards Friedrich

Friedrich
+6  A: 

I generally use what is sometimes called the 'right hand clockwise rule'. It goes like this:

  • Start from the identifier.
  • Go to the immediate right of it.
  • Then move clockwise and come to the left hand side.
  • Move clockwise and come to the right side.
  • Do this as long as the declaration has not been parsed fully.

There's an additional meta-rule that has to be taken care of:

  • If there are parantheses, complete each level of parantheses before moving out.

Here, 'going' and 'moving' somewhere means reading the symbol there. The rules for that are:

* : pointer to

() : function returning

(int, int) : function taking two ints and returning

int, char, etc. : int, char, etc.

[] : array of

[10] : array of ten

etc.

So, for example, int* (*xyz[10])(int*, char) is read as: xyz is an

array of ten

pointers to

functions taking an int* and a char and returning

an int*

sundar
@sundar: very nice reference!
Lazer
@sundar: Can you extend it to include multi-dimensional arrays and function pointers? What about `extern char *const (*goop( char *b ))( int, long );`? (picked up from http://www.ericgiguere.com/articles/reading-c-declarations.html)
Lazer
+2  A: 

http://eli.thegreenplace.net/2008/07/18/reading-c-type-declarations/

Eli Bendersky
This one. This one explains it all.
elcuco
A: 

Automated solution is cdecl.

In general, you declare a variable the way you use it. For example, you dereference a pointer p as in:

char c = * p

you declare it in a similar looking way:

char * p;

Same goes for hairy function pointers. Let's declare f to be good old "pointer to function returning pointer to int," and an external declaration just to be funny. It's a pointer to a function, so we start with:

extern * f();

It returns a pointer to an int, so somewhere in front there there's

extern int * * f(); // XXX not quite yet

Now which is the correct associativity? I can never remember, so use some parenthesis.

extern (int *)(* f)();

Declare it the way you use it.

jfm3
A: 

Read from right to left.

***code[][]
  • code[][] is a multidimenstional array
  • *code[][] is a multidimenstional array pointer
  • **code[][] is a multidimenstional array pointer to a pointer
  • ***code[][] is a multidimenstional array pointer to a pointer to a pointer
MrValdez
A: 

Just came across an illuminating section in "The Development of the C Language":

For each object of such a composed type, there was already a way to mention the underlying object: index the array, call the function, use the indirection operator on the pointer. Analogical reasoning led to a declaration syntax for names mirroring that of the expression syntax in which the names typically appear. Thus,

int i, *pi, **ppi;

declare an integer, a pointer to an integer, a pointer to a pointer to an integer. The syntax of these declarations reflects the observation that i, *pi, and **ppi all yield an int type when used in an expression. Similarly,

int f(), *f(), (*f)();

declare a function returning an integer, a function returning a pointer to an integer, a pointer to a function returning an integer;

int *api[10], (*pai)[10];

declare an array of pointers to integers, and a pointer to an array of integers. In all these cases the declaration of a variable resembles its usage in an expression whose type is the one named at the head of the declaration.

outis