How do you read C declarations?
Asked Answered
R

10

49

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?

Remittance answered 18/9, 2008 at 0:57 Comment(5)
Eyes work... But if you're stuck using Windows, i suppose you could try Narrator.Vaudois
So does "three star programmer" mean there are at least three stars in all the types he declares?Mohave
pretty much, it is an old school insult for a bad c programmerRemittance
Here's a website to automate this for you!Coley
And, for ***code[][], it returns syntax error :-) A damnedly useful site, though.Allgood
S
38

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

  1. Find the identifier. This is your starting point. On a piece of paper, write "declare identifier as".
  2. Look to the right. If there is nothing there, or there is a right parenthesis ")", goto step 4.
  3. You are now positioned either on an array (left bracket) or function (left parenthesis) descriptor. There may be a sequence of these, ending either with an unmatched right parenthesis or the end of the declarator (a semicolon or a "=" for initialization). For each such descriptor, reading from left to right:

    • if an empty array "[]", write "array of"
    • if an array with a size, write "array size of"
    • if a function "()", write "function returning"

    Stop at the unmatched parenthesis or the end of the declarator, whichever comes first.

  4. Return to the starting position and look to the left. If there is nothing there, or there is a left parenthesis "(", goto step 6.
  5. You are now positioned on a pointer descriptor, "*". There may be a sequence of these to the left, ending either with an unmatched left parenthesis "(" or the start of the declarator. Reading from right to left, for each pointer descriptor write "pointer to". Stop at the unmatched parenthesis or the start of the declarator, whichever is first.
  6. At this point you have either a parenthesized expression or the complete declarator. If you have a parenthesized expression, consider it as your new starting point and return to step 2.
  7. Write down the type specifier. Stop.

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

Spray answered 18/9, 2008 at 1:7 Comment(1)
There seem to be many implementations of cdecl, even one in the K&R book. But all of them just translate to/from plain English. I'd like however to translate to/from an internal representation, like a compiler does. I mean, read a C declaration, and "encode" it in some sort of data structure. What data structure would be needed to represent any C declaration?Youth
D
27

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 parentheses, complete each level of parentheses 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

pointer to

function taking an int* and a char and returning

an int*

Dibble answered 18/9, 2008 at 7:14 Comment(2)
@sundar: very nice reference!Grommet
@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 ericgiguere.com/articles/reading-c-declarations.html)Grommet
J
8

One word: cdecl

Damnit, beaten by 15 seconds!

Jariah answered 18/9, 2008 at 1:2 Comment(1)
See my comment above. For ***code[][], it returns syntax error :-) A damnedly useful site, thoughAllgood
M
4

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

Mcdonough answered 18/9, 2008 at 1:3 Comment(0)
M
4

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.

Marillin answered 18/9, 2008 at 6:41 Comment(0)
A
3

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.

Allhallowtide answered 18/9, 2008 at 1:1 Comment(0)
L
1

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.

Lamaism answered 20/9, 2008 at 3:48 Comment(0)
I
1

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.

Isidroisinglass answered 5/12, 2009 at 7:11 Comment(0)
S
1

There's also a Web-based version of cdecl which is pretty slick.

Selfish answered 27/5, 2011 at 4:15 Comment(0)
J
0

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.

Jarvisjary answered 18/9, 2008 at 1:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.