505

What is the difference between the following declarations:

int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);

What is the general rule for understanding more complex declarations?

2
  • 64
    Here is a great article about reading complex declarations in C: unixwiz.net/techtips/reading-cdecl.html
    – jesper
    Commented Jan 27, 2013 at 13:00
  • @jesper Unfortunately, the const and volatile qualifiers, which are both important and tricky, are missing in that article.
    – not-a-user
    Commented May 18, 2017 at 15:32

12 Answers 12

474
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers

The third one is same as the first.

The general rule is operator precedence. It can get even much more complex as function pointers come into the picture.

6
  • 5
    So, for 32 bit systems: int* arr[8]; /* 8x4 bytes allocated, for each pointer / int (*arr)[8]; / 4 bytes allocated, only a pointer */
    – George
    Commented May 13, 2009 at 18:43
  • 11
    Nope. int* arr[8]: 8x4 bytes allocated total, 4 bytes for each pointer. int (*arr)[8] is right, 4 bytes. Commented May 13, 2009 at 18:49
  • 2
    I should have re-read what I wrote. I meant 4 for each pointer. Thanks for the help!
    – George
    Commented May 13, 2009 at 18:59
  • 5
    The reason that the first one is the same as the last is that it's always allowed to wrap parentheses around declarators. P[N] is an array declarator. P(....) is a function declarator and *P is a pointer declarator. So everything in the following is the same as without any parentheses (except for the one of the functions' "()": int (((*p))); void ((g(void))); int *(a[1]); void (*(p())). Commented May 13, 2009 at 20:21
  • 2
    Well done in your explanation. For an in-depth reference on precedence and associativity of operators refer to page 53 of The C Programming Language (ANSI C second edition) by Brian Kernighan and Dennis Ritchie. The operators ( ) [ ] associate left to right and have higher precedence than * so read int* arr[8] as an array of size 8 where each element points to an int and int (*arr)[8] as a pointer to an array of size 8 that holds integers
    – Mushy
    Commented Sep 23, 2017 at 14:03
277

Use the cdecl program, as suggested by K&R.

$ cdecl
Type `help' or `?' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>

It works the other way too.

cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
4
  • 1
    @ankii Most Linux distributions should have a package. You could also build your own binary.
    – sigjuice
    Commented Oct 15, 2019 at 17:05
  • ah sorry for not mentioning, macOS here. will see if available, otherwise website is fine too. ^^ thanks for letting me know about this.. Feel free to flag NLN .
    – user10063119
    Commented Oct 15, 2019 at 17:06
  • 2
    @ankii You can install from Homebrew (and maybe MacPorts?). If those are not to your taste, it is trivial to build your own from the Github link on the top right of cdecl.org (I just built it on macOS Mojave). Then just copy the cdecl binary to your PATH. I recommend $PATH/bin, because there is no need to involve root in something as simple as this.
    – sigjuice
    Commented Oct 15, 2019 at 17:18
  • Oh hadn't read the little paragraph about installation in readme. just some commands and flags for handling dependencies.. Installed using brew. :)
    – user10063119
    Commented Oct 15, 2019 at 17:35
134

I don't know if it has an official name, but I call it the Right-Left Thingy(TM).

Start at the variable, then go right, and left, and right...and so on.

int* arr1[8];

arr1 is an array of 8 pointers to integers.

int (*arr2)[8];

arr2 is a pointer (the parenthesis block the right-left) to an array of 8 integers.

int *(arr3[8]);

arr3 is an array of 8 pointers to integers.

This should help you out with complex declarations.

7
  • 20
    I have heard it referred to by the name of "The Spiral Rule", which can be found here.
    – fouric
    Commented May 27, 2013 at 3:04
  • 7
    @InkBlend: The spiral rule is different from right-left rule. The former fails in cases like int *a[][10] while the latter succeeds.
    – legends2k
    Commented Oct 9, 2013 at 13:51
  • 1
    As InkBlend and legends2k said, this is Spiral Rule which is more complex and doesn't work in all cases, so there is no reason to use it.
    – kotlomoy
    Commented Sep 25, 2014 at 18:29
  • Don't forget the lef to right associativity of ( ) [ ] and right to left of * &
    – Mushy
    Commented Sep 23, 2017 at 14:10
  • @legends2k : What is the declaration readout for int *a[][10]
    – beastboy
    Commented Nov 21, 2018 at 4:37
29
int *a[4]; // Array of 4 pointers to int

int (*a)[4]; //a is a pointer to an integer array of size 4

int (*a[8])[5]; //a is an array of pointers to integer array of size 5 
3
  • Shouldn't the 3rd one be: a is an array of pointers to integer array of size 8 ? I mean each of the integer arrays will be of size 8 right? Commented May 2, 2012 at 7:47
  • 2
    @Rushil: no, the last subscript ([5]) represents the inner dimension. This means that (*a[8]) is the first dimension, and is thus the outer representation of the array. What each element within a points to is a different integer array of size 5.
    – zeboidlund
    Commented Jan 25, 2013 at 17:19
  • Thanks for the third one. I'm looking for how to write array of pointers to array.
    – Deqing
    Commented Aug 6, 2013 at 14:39
16

The answer for the last two can also be deducted from the golden rule in C:

Declaration follows use.

int (*arr2)[8];

What happens if you dereference arr2? You get an array of 8 integers.

int *(arr3[8]);

What happens if you take an element from arr3? You get a pointer to an integer.

This also helps when dealing with pointers to functions. To take sigjuice's example:

float *(*x)(void )

What happens when you dereference x? You get a function that you can call with no arguments. What happens when you call it? It will return a pointer to a float.

Operator precedence is always tricky, though. However, using parentheses can actually also be confusing because declaration follows use. At least, to me, intuitively arr2 looks like an array of 8 pointers to ints, but it is actually the other way around. Just takes some getting used to. Reason enough to always add a comment to these declarations, if you ask me :)

edit: example

By the way, I just stumbled across the following situation: a function that has a static matrix and that uses pointer arithmetic to see if the row pointer is out of bounds. Example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0]))

int *
put_off(const int newrow[2])
{
    static int mymatrix[3][2];
    static int (*rowp)[2] = mymatrix;
    int (* const border)[] = mymatrix + NUM_ELEM(mymatrix);

    memcpy(rowp, newrow, sizeof(*rowp));
    rowp += 1;
    if (rowp == border) {
        rowp = mymatrix;
    }

    return *rowp;
}

int
main(int argc, char *argv[])
{
    int i = 0;
    int row[2] = {0, 1};
    int *rout;

    for (i = 0; i &lt; 6; i++) {
        row[0] = i;
        row[1] += i;
        rout = put_off(row);
        printf("%d (%p): [%d, %d]\n", i, (void *) rout, rout[0], rout[1]);
    }

    return 0;
}

Output:

0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]

Note that the value of border never changes, so the compiler can optimize that away. This is different from what you might initially want to use: const int (*border)[3]: that declares border as a pointer to an array of 3 integers that will not change value as long as the variable exists. However, that pointer may be pointed to any other such array at any time. We want that kind of behaviour for the argument, instead (because this function does not change any of those integers). Declaration follows use.

(p.s.: feel free to improve this sample!)

5
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
3

As a rule of thumb, right unary operators (like [], (), etc) take preference over left ones. So, int *(*ptr)()[]; would be a pointer that points to a function that returns an array of pointers to int (get the right operators as soon as you can as you get out of the parenthesis)

4
  • That is true, but it is also ilegal. You can not have a function that returns an array. I tried and got this: error: ‘foo’ declared as function returning an array int foo(int arr_2[5][5])[5]; under GCC 8 with $ gcc -std=c11 -pedantic-errors test.c Commented Feb 24, 2019 at 1:48
  • 1
    The reason of the compiler to give that error is that it is interpreting the function as returning an array, as the correct interpretation of the precedence rule states. It is ilegal as a declaration, but the legal declaration int *(*ptr)(); allows an expression like p()[3] (or (*p)()[3]) to be used later. Commented Feb 25, 2019 at 19:39
  • Ok, if I understand it, you are talking about creating a function that returns a pointer to the first element of an array (not an array itself), and later use that function as if it were returning an array? Interesting idea. I'll try it. int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); } and call it like this: foo(arr)[4]; which should contain arr[2][4], right? Commented Feb 26, 2019 at 11:35
  • right... but you were right also, and the declaration was ilegal. :) Commented Feb 27, 2019 at 4:53
2

I think we can use the simple rule ..

example int * (*ptr)()[];
start from ptr 

" ptr is a pointer to " go towards right ..its ")" now go left its a "(" come out go right "()" so " to a function which takes no arguments " go left "and returns a pointer " go right "to an array" go left " of integers "

1
  • I would improve that a little bit: "ptr is a name that refers to" go right... it's ), now go left... it's * "a pointer to" go right... it's ), now go left... it's a ( come out, go right () so " to a function which takes no arguments " go right... [] "and returns an array of" go right ; end, so go left... * "pointers to" go left... int "integers" Commented Feb 26, 2019 at 14:56
2

Here's how I interpret it:

int *something[n];

Note on precedence: array subscript operator ([]) has higher priority than dereference operator (*).

So, here we will apply the [] before *, making the statement equivalent to:

int *(something[i]);

Note on how a declaration makes sense: int num means num is an int, int *ptr or int (*ptr) means, (value at ptr) is an int, which makes ptr a pointer to int.

This can be read as, (value of the (value at ith index of the something)) is an integer. So, (value at the ith index of something) is an (integer pointer), which makes the something an array of integer pointers.

In the second one,

int (*something)[n];

To make sense out of this statement, you must be familiar with this fact:

Note on pointer representation of array: somethingElse[i] is equivalent to *(somethingElse + i)

So, replacing somethingElse with (*something), we get *(*something + i), which is an integer as per declaration. So, (*something) given us an array, which makes something equivalent to (pointer to an array).

0

I guess the second declaration is confusing to many. Here's an easy way to understand it.

Lets have an array of integers, i.e. int B[8].

Let's also have a variable A which points to B. Now, value at A is B, i.e. (*A) == B. Hence A points to an array of integers. In your question, arr is similar to A.

Similarly, in int* (*C) [8], C is a pointer to an array of pointers to integer.

0
int *arr1[5]

In this declaration, arr1 is an array of 5 pointers to integers. Reason: Square brackets have higher precedence over * (dereferncing operator). And in this type, number of rows are fixed (5 here), but number of columns is variable.

int (*arr2)[5]

In this declaration, arr2 is a pointer to an integer array of 5 elements. Reason: Here, () brackets have higher precedence than []. And in this type, number of rows is variable, but the number of columns is fixed (5 here).

-9

In pointer to an integer if pointer is incremented then it goes next integer.

in array of pointer if pointer is incremented it jumps to next array

1
  • 1
    "in array of pointer if pointer is incremented it jumps to next array" this is plain wrong.
    – alk
    Commented Aug 25, 2017 at 13:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.