tags:

views:

14557

answers:

5

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?

+8  A: 
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.

Mehrdad Afshari
So, for 32 bit systems:int* arr[8]; /* 8x4 bytes allocated, for each pointer */int (*arr)[8]; /* 4 bytes allocated, only a pointer */
Richard J. Terrell
Nope. int* arr[8]: 8x4 bytes allocated *total*, 4 bytes for each pointer. int (*arr)[8] is right, 4 bytes.
Mehrdad Afshari
I should have re-read what I wrote. I meant 4 for each pointer. Thanks for the help!
Richard J. Terrell
Yeah, that's correct.
Mehrdad Afshari
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())).
Johannes Schaub - litb
+1 for the well written supporting reference. Other articles at that site look worth knowing about too.
RBerteig
+13  A: 

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.

GMan
It is called reading "boustrophedonically" :)
dogeen
+25  A: 

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 )
sigjuice
wow, great tool, thanks!
Richard J. Terrell
amazing tool :)
nXqd
+1  A: 

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 < 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!)

Hraban Luyat
A: 
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
Byron Formwalt