Reading C type declarations

July 18th, 2008 at 11:22 am

C is not an easy language to parse. A cursory glance at its BNF grammar should convince anyone immediately. One of the hairiest parts of the grammar is type declarations.

The creators of C, Brian Kernighan and Dennis Ritchie, admit it themselves in the beginning of section 5.12 of K&R2 (“The C programming language 2nd Ed”):

C is sometimes castigated for the syntax of its declarations, particularly ones that involve pointers to functions. The syntax is an attempt to make the declaration and the use agree; it works well for simple cases, but it can be confusing for the harder ones, because declarations cannot be read left to right, and because parentheses are over-used.

Quick, what is the type of foo here:

char *(*(**foo [][8])())[];

Oh, you didn’t know it’s foo is array of array of 8 pointer to pointer to function returning pointer to array of pointer to char ? Shame on you…

Seriously, though, type declarations in C are complex, and sometimes aren’t intuitive. There is, however, a relatively simple method of reading them.

First of all, declarations consist of a basic type and modifiers:

/* int is a basic type */
int x;     

/* [] is the 'array of' modifier */
int x[5]; 

/* * is the 'pointer to' modifier */
int *x;

/* () is the 'function returning...' modifier' */
int (*fptr)(void);

When you see a complex declaration, first recognize the basic type and the variable name. In:

int (*x)[10][20];

The basic type is int and the variable name is x. So the declaration means x is … int for some yet unknown value of

To read the modifiers, go to the right from the variable name until you can – that is, until you run into a semicolon or a closing right parenthesis. When you reach one of these stops, start going left until you reach an opening left parenthesis (or the basic type, in which case you’re done). Each time you see a new modifier (either going right or left), attach it to the end of the current declaration sentence.

Let’s see some examples:

/* x is int (but that was easy...) */
int x;

/* go right from 'x' - we hit the array
   and then get stuck on the ';', so 
   we start going left, where there's
   nothing.
   
   so:
   
   x is an array[5] of int 
*/
int x[5];

/* there's nothing to the right, but a '*'
   to the left, so:
   
   x is a pointer to int
*/
int *x;

/* now, combining these cases:
   
   x is an array[5] of pointer to int
*/
int *x[5];

/* how about this ?
  
   x is an array[5] of array[2] of int
*/
int x[5][2];

/* hey, this is becoming easy...

   x is an array[5] of array[2] of pointer
     to pointer to int
*/
int **x[5][2];

/* grouping parantheses complicate things,
   but not too much.
   trying to go right from 'x', we hit the
   closing paren, so we go left. After
   we attach the pointer we see an opening
   paren, so we can go right again:
   
   x is a pointer to array[5] of int
*/
int (*x)[5];

/* function declarations are just like arrays:
   we go right from 'x', and attach 'array[4] of'
   then we hit the paren, and go left, attaching
   'pointer to'. Then, we hit the left paren, so
   we go right again, attaching 
   'function(char, int) returning'
   
   And eventually:
   
   x is an array[4] of pointer to 
     function(char, int) returning int
*/
int (*x[4])(char, int);

I hope you’re now convinced that the task of understanding C type declarations isn’t that difficult.

Some final notes:

  1. If you really want to understand what’s going under the hood of C type declarations, read sections A.8.5 and A.8.6 of K&R2. Also, section 5.12 contains a program that translates declarations into words.
  2. This page was very useful in the preparation of the article. Thanks to Steve Friedl for sharing it
  3. As some commenters kindly noted, other good sources of information on this topic are book “Expert C Programming” by Peter Van Der Linden (in chapter 3), and the unix command cdecl(1).
  4. I can’t imagine why you would ever need a type as complex as the initial example of this article, but if you do, the best way is to build the type incrementally using typedef declarations.

Related posts:

  1. Implementing cdecl with pycparser
  2. Correct usage of const with pointers
  3. How Clang handles the type / variable name ambiguity of C/C++
  4. Allocating multi-dimensional arrays in C++
  5. The type / variable name ambiguity in C++

15 Responses to “Reading C type declarations”

  1. tritonNo Gravatar Says:

    good job man! this was a useful post. though i’ve read it on ‘Expert C Programming’ book. :D

  2. Remko TronçonNo Gravatar Says:

    I was about to say the same thing: If you want another good tutorial on reading C types, check out the excellent ‘Expert C Programming’ by Peter van der Linden.

  3. Pádraig BradyNo Gravatar Says:

    $ cdecl explain “char *(*(**foo [][8])())[];”
    declare foo as array of array 8 of pointer to pointer to function returning pointer to array of pointer to char

  4. miroNo Gravatar Says:

    Heh or just use a typedef :D
    Granted it is complex (pascal has much nicer rules).

  5. bernzNo Gravatar Says:

    Anyone who actually uses “char *(*(**foo [][8])())[]” must be smoking some pretty heavy stuff. :-) Tip: When pretending you are a C parser, read type definitions from the inside, out.

  6. Kia KroasNo Gravatar Says:

    I’m not usually one to point out typos, but…in your second code box, you have inx instead of int.
    “inx x[5];”
    s/inx/int/

    Otherwise, it was a nice, useful post.

  7. elibenNo Gravatar Says:

    Thanks for all the comments (here and on Proggit)- I’ve incorporated some of the information into the article.

    Kia Kroas: why, pointing out typos *is* important. Thanks, fixed.

  8. Miron BrezuleanuNo Gravatar Says:

    What about declarations that do not have a variable identifier? Example:
    int f(int (*)());

    This declares a function that return an int and takes a function pointer as argument. The function pointer is a nested declaration, and it doesn’t use a variable name. How do you figure out where to start when parsing the nested declaration?

  9. Jestine PaulNo Gravatar Says:

    I sometimes follow this brief description from http://klausler.com/cnotes.txt when reading type declarations

    How to easily read a declaration from left to right:
    — transform function argument types from inside out first
    — move the base type to the end
    — add outer parentheses if there’s an initial *
    — change every (*…) to … ->
    ———— one -> for each *
    ———— move qualifiers, so * const becomes const ->

  10. elibenNo Gravatar Says:

    Miron,

    This is called an “abstract declarator” in the jargon of K&R2, as opposed to a “declarator” which has a variable name.

    1. Go into the innermost grouping parentheses. If there are none, ignore this step.
    2. The virtual declarator would appear between the *s and the []s
    3. Proceed decoding as a declarator.

  11. Miron BrezuleanuNo Gravatar Says:

    Thanks for the reply,

    I’ve used this as a Haskell programming exercise.
    http://haskell.pastebin.com/f4c0d351c

    It ended up as a simple C->English cdecl.

    Maybe you’re interested.

  12. AatishNo Gravatar Says:

    Very nice blog. Loved it!
    Thanks.

  13. AlexNo Gravatar Says:

    I’ve seen the declaration char *(*(**foo [][8])())[]; many times on many sites.
    No one seems to dare giving a working code fragment that uses this array. I wonder, may be you will try.
    Here is an example of much simpler type declaration, but you can compile and execute it.

    char* f1(char* s) 
    {
     char* buf = new char[4 + strlen(s)];
     return strcat(strcpy(buf, "Yes, "), s);
    }
    char* f2(char* s) 
    {
     char* buf = new char[4 + strlen(s)];
     return strcat(strcpy(buf, "Bye, "), s);
    }
    char* f3(char* s)
    {
      char* buf = new char[4 + strlen(s)];
      return strcat(strcpy(buf, "Hi, "), s);
    }
    
    void main ()
    {
      puts("\nPointer to array of pointers to functions");
      char* (*bar [3])(char*) = { f3, f2, f1 }; // Array of pointers to functions 
      char* (**foo [3])(char*) = { bar, bar+2, bar+1 };
    
      for(int i=0; i<3; i++)
        printf("%s\n", (*foo[i])("Alex"));
    
      puts("\n\n");
    }

    It would be interesting to see working example with a char *(*(**foo [][8])())[]; type.
    Thanks in advance.

  14. elibenNo Gravatar Says:

    Alex

    I don’t think that finding a real use for such declaration is very important. It seems to me that it is being used as the example in many places because it contains almost any imaginable complication of C declarations.

  15. Edmond MajorNo Gravatar Says:

    Articles like this are why I love this website ;D