What is the difference between
int * a[10];
and
int (*b)[10];
I know that the first one is an array of pointers to integers, but what is the second one? If I try assigning
int (*c)[10] = a;
what am I actually doing to c?
See if you can install the cdecl command for your system. (On Ubuntu, sudo apt-get install cdecl.) There's also a web interface at cdecl.org.
Here's what it told me for your examples on my system:
$ cdecl
Type `help' or `?' for help
cdecl> explain int * a[10];
declare a as array 10 of pointer to int
cdecl> explain int (*b)[10];
declare b as pointer to array 10 of int
cdecl>
Second one is a pointer to an array of 10 integers. Where? God knows; you never initialized it.
If you assign a to it, you're making it point to the same array of 10 integers that a pointed to... nothing fancy.
I once learned a fancy trick (maybe that's not the right word) about reading c declarations that really stuck with me: http://www.antlr.org/wiki/display/CS652/How+To+Read+C+Declarations
Start at the variable name (or innermost construct if no identifier is
present. Look right without jumping over a right parenthesis; say what
you see. Look left again without jumping over a parenthesis; say what
you see. Jump out a level of parentheses if any. Look right; say what
you see. Look left; say what you see. Continue in this manner until
you say the variable type or return type.
Using that mental algorithm, you can easily read and understand any C variable declaration.
Sometimes, when they get really complicated, it's nice to use this little utility: cdecl which is available as a stand-alone app, but also as a website: http://cdecl.org/
int *a[10] is an array of pointers, i.e. a pointer array which can hold 10 different integer memory allocations (spaces), viz
a[0] = (int *)calloc(10, sizeof(int));
a[1] = (int *)calloc(50, sizeof(int));
and so on.. and generally the sizeof a will be (10 * 4) => 40.
int (*b)[10] is a pointer to array that points to an array of integer i.e. b points to integer array of size 10, viz,
b = &(integer array[10]);
the above can be used as in the given example:
int c[10];
int (*b)[10];
b = &c;
The advantage of using the pointer to array is that, the array can be changed from time to time during the course of execution according to the need of the code.
The first one is an array of pointers to 10 integers.
The second one is one pointer to an array containing five integers.
The last is second with initializing the one pointer to point to a, a here is an array of pointers to 10 integers not an array of 10 integers so be careful.
You should be doing this:
int d[10];
int (*c)[10] = d;
For more:
C pointer to array/array of pointers disambiguation
Related
I started learning C recently, and I'm having a problem understanding pointer syntax, for example when I write the following line:
int ** arr = NULL;
How can I know if:
arr is a pointer to a pointer of an integer
arr is a pointer to an array of pointers to integers
arr is a pointer to an array of pointers to arrays of integers
Isn't it all the same with int ** ?
Another question for the same problem:
If I have a function that receives char ** s as a parameter, I want to refer to it as a pointer to an array of strings, meaning a pointer to an array of pointers to an array of chars, but is it also a pointer to a pointer to a char?
Isn't it all the same with int **?
You've just discovered what may be considered a flaw in the type system. Every option you specified can be true. It's essentially derived from a flat view of a programs memory, where a single address can be used to reference various logical memory layouts.
The way C programmers have been dealing with this since C's inception, is by putting a convention in place. Such as demanding size parameter(s) for functions that accept such pointers, and documenting their assumptions about the memory layout. Or demanding that arrays be terminated with a special value, thus allowing "jagged" buffers of pointers to buffers.
I feel a certain amount of clarification is in order. As you'd see when consulting the other very good answers here, arrays are most definitely not pointers. They do however decay into ones in enough contexts to warrant a decades long error in teaching about them (but I digress).
What I originally wrote refers to code as follows:
void func(int **p_buff)
{
}
//...
int a = 0, *pa = &a;
func(&pa);
//...
int a[3][10];
int *a_pts[3] = { a[0], a[1], a[2] };
func(a_pts);
//...
int **a = malloc(10 * sizeof *a);
for(int i = 0; i < 10; ++i)
a[i] = malloc(i * sizeof *a[i]);
func(a);
Assume func and each code snippet is compiled in a separate translation unit. Each example (barring any typos by me) is valid C. The arrays will decay into a "pointer-to-a-pointer" when passed as arguments. How is the definition of func to know what exactly it was passed from the type of its parameter alone!? The answer is that it cannot. The static type of p_buff is int**, but it still allows func to indirectly access (parts of) objects with vastly different effective types.
The declaration int **arr says: "declare arr as a pointer to a pointer to an integer". It (if valid) points to a single pointer that points (if valid) to a single integer object. As it is possible to use pointer arithmetic with either level of indirection (i.e. *arr is the same as arr[0] and **arr is the same as arr[0][0]) , the object can be used for accessing any of the 3 from your question (that is, for second, access an array of pointers to integers, and for third, access an array of pointers to first elements of integer arrays), provided that the pointers point to the first elements of the arrays...
Yet, arr is still declared as a pointer to a single pointer to a single integer object. It is also possible to declare a pointer to an array of defined dimensions. Here a is declared as a pointer to 10-element array of pointers to arrays of 10 integers:
cdecl> declare a as pointer to array 10 of pointer to array 10 of int;
int (*(*a)[10])[10]
In practice array pointers are most used for passing in multidimensional arrays of constant dimensions into functions, and for passing in variable-length arrays. The syntax to declare a variable as a pointer to an array is seldom seen, as whenever they're passed into a function, it is somewhat easier to use parameters of type "array of undefined size" instead, so instead of declaring
void func(int (*a)[10]);
one could use
void func(int a[][10])
to pass in a a multidimensional array of arrays of 10 integers. Alternatively, a typedef can be used to lessen the headache.
How can I know if :
arr is a pointer to a pointer of an integer
It is always a pointer to pointer to integer.
arr is a pointer to an array of pointers to integers
arr is a pointer to an array of pointers to arrays of integers
It can never be that. A pointer to an array of pointers to integers would be declared like this:
int* (*arr)[n]
It sounds as if you have been tricked to use int** by poor teachers/books/tutorials. It is almost always incorrect practice, as explained here and here and (
with detailed explanation about array pointers) here.
EDIT
Finally got around to writing a detailed post explaining what arrays are, what look-up tables are, why the latter are bad and what you should use instead: Correctly allocating multi-dimensional arrays.
Having solely the declaration of the variable, you cannot distinguish the three cases. One can still discuss if one should not use something like int *x[10] to express an array of 10 pointers to ints or something else; but int **x can - due to pointer arithmetics, be used in the three different ways, each way assuming a different memory layout with the (good) chance to make the wrong assumption.
Consider the following example, where an int ** is used in three different ways, i.e. p2p2i_v1 as a pointer to a pointer to a (single) int, p2p2i_v2 as a pointer to an array of pointers to int, and p2p2i_v3 as a pointer to a pointer to an array of ints. Note that you cannot distinguish these three meanings solely by the type, which is int** for all three. But with different initialisations, accessing each of them in the wrong way yields something unpredictable, except accessing the very first elements:
int i1=1,i2=2,i3=3,i4=4;
int *p2i = &i1;
int **p2p2i_v1 = &p2i; // pointer to a pointer to a single int
int *arrayOfp2i[4] = { &i1, &i2, &i3, &i4 };
int **p2p2i_v2 = arrayOfp2i; // pointer to an array of pointers to int
int arrayOfI[4] = { 5,6,7,8 };
int *p2arrayOfi = arrayOfI;
int **p2p2i_v3 = &p2arrayOfi; // pointer to a pointer to an array of ints
// assuming a pointer to a pointer to a single int:
int derefi1_v1 = *p2p2i_v1[0]; // correct; yields 1
int derefi1_v2 = *p2p2i_v2[0]; // correct; yields 1
int derefi1_v3 = *p2p2i_v3[0]; // correct; yields 5
// assuming a pointer to an array of pointers to int's
int derefi1_v1_at1 = *p2p2i_v1[1]; // incorrect, yields ? or seg fault
int derefi1_v2_at1 = *p2p2i_v2[1]; // correct; yields 2
int derefi1_v3_at1 = *p2p2i_v3[1]; // incorrect, yields ? or seg fault
// assuming a pointer to an array of pointers to an array of int's
int derefarray_at1_v1 = (*p2p2i_v1)[1]; // incorrect; yields ? or seg fault;
int derefarray_at1_v2 = (*p2p2i_v2)[1]; // incorrect; yields ? or seg fault;
int derefarray_at1_v3 = (*p2p2i_v3)[1]; // correct; yields 6;
How can I know if :
arr is a pointer to a pointer of an integer
arr is a pointer to an array of pointers to integers
arr is a pointer to an array of pointers to arrays of integers
You cannot. It can be any of those. What it ends up being depends on how you allocate / use it.
So if you write code using these, document what you're doing with them, pass size parameters to the functions using them, and generally be sure about what you allocated before using it.
Pointers do not keep the information whether they point to a single object or an object that is an element of an array. Moreover for the pointer arithmetic single objects are considered like arrays consisting from one element.
Consider these declarations
int a;
int a1[1];
int a2[10];
int *p;
p = &a;
//...
p = a1;
//...
p = a2;
In this example the pointer p deals with addresses. It does not know whether the address it stores points to a single object like a or to the first element of the array a1 that has only one element or to the first element of the array a2 that has ten elements.
The type of
int ** arr;
only have one valid interpretation. It is:
arr is a pointer to a pointer to an integer
If you have no more information than the declaration above, that is all you can know about it, i.e. if arr is probably initialized, it points to another pointer, which - if probably initialized - points to an integer.
Assuming proper initialization, the only guaranteed valid way to use it is:
**arr = 42;
int a = **arr;
However, C allows you to use it in multiple ways.
• arr can be used as a pointer to a pointer to an integer (i.e. the basic case)
int a = **arr;
• arr can be used as a pointer to a pointer to an an array of integer
int a = (*arr)[4];
• arr can be used as a pointer to an array of pointers to integers
int a = *(arr[4]);
• arr can be used as a pointer to an array of pointers to arrays of integers
int a = arr[4][4];
In the last three cases it may look as if you have an array. However, the type is not an array. The type is always just a pointer to a pointer to an integer - the dereferencing is pointer arithmetic. It is nothing like a 2D array.
To know which is valid for the program at hand, you need to look at the code initializing arr.
Update
For the updated part of the question:
If you have:
void foo(char** x) { .... };
the only thing that you know for sure is that **x will give a char and *x will give you a char pointer (in both cases proper initialization of x is assumed).
If you want to use x in another way, e.g. x[2] to get the third char pointer, it requires that the caller has initialized x so that it points to a memory area that has at least 3 consecutive char pointers. This can be described as a contract for calling foo.
C syntax is logical. As an asterisk before the identifier in the declaration means pointer to the type of the variable, two asterisks mean pointer to a pointer to the type of the variable.
In this case arr is a pointer to a pointer to integer.
There are several usages of double pointers. For instance you could represent a matrix with a pointer to a vector of pointers. Each pointer in this vector points to the row of the matrix itself.
One can also create a two dimensional array using it,like this
int **arr=(int**)malloc(row*(sizeof(int*)));
for(i=0;i<row;i++) {
*(arr+i)=(int*)malloc(sizeof(int)*col); //You can use this also. Meaning of both is same. //
arr[i]=(int*)malloc(sizeof(int)*col); }
There is one trick when using pointers, read it from right hand side to the left hand side:
int** arr = NULL;
What do you get: arr, *, *, int, so array is a pointer to a pointer to an integer.
And int **arr; is the same as int** arr;.
int ** arr = NULL;
It's tell the compiler, arr is a double pointer of an integer and assigned NULL value.
There are already good answers here, but I want to mention my "goto" site for complicated declarations: http://cdecl.org/
Visit the site, paste your declaration and it will translate it to English.
For int ** arr;, it says declare arr as pointer to pointer to int.
The site also shows examples. Test yourself on them, then hover your cursor to see the answer.
(double (^)(int , long long ))foo
cast foo into block(int, long long) returning double
int (*(*foo)(void ))[3]
declare foo as pointer to function (void) returning pointer to array 3 of int
It will also translate English into C declarations, which is prety neat - if you get the description correct.
This question already has answers here:
C pointer to array/array of pointers disambiguation
(13 answers)
what is difference between defining char a[5] and char (*a)[5]? [duplicate]
(4 answers)
Closed 8 years ago.
When I read books about C language, the two level pointer bothered me a lot.
char s[5][5];
char *s[5];
char (*s)[5];
so what is the difference between them?
In C, it is better to speak out the declaration. Then, it becomes intuitive. For this, you follow the right-left convention. Here is how it goes:
char *s[5];
How do you speak it? For that you start at the right of the variable name s and then go to the left. So you start by saying "s is a/an", On the right, you see a [] and you say "s is an array ... ". And then you go to the left and see the *, and say "s is an array of pointers." And that is what it is. It si an array of 5 pointers. You can store different pointers in this array.
Now for the other one:
char (*s)[5];
You start the same way. "s is a/an", and then look at the (). Anything within the () is bound to s closer than anything outside. So the * is more closely bound with s than []. So You now say, "s is a pointer ..." and now you go out of the parenthesis and see the []. So you continue, "s is a pointer to an array". And that is exactly what it is. It is a pointer, which will point to the first element of an array.
Now follow the same logic and try to guess what the following will be:
int (*callme)(int a, int b)
int (*callme[10])(int a, int b)
Hint, the last one can be used to create lookup tables for functions.
Edit:
As mentioned in the comments, there is also a char in the beginning. I have never ben able to figure out an easy way of speaking this, but is generally clear from the context. For example, in the first example, the char defines the type of the array, while in the second example, it defines pointer. In the exercises I have posted, the int defines the type for the return values of the functions. Generally with definitions such as these, there will be exactly one item with the undefined type. And thats how I figure out where the type goes.
first is 2 dimensional array of char
second is array of pointer to char
third is pointer to an array of char
While declaration was covered, perhaps differences in usage should also be pointed out:
char s[5][5]; --
s points to an area of allocated memory (heap if global, stack if local),
you may safely write up to 25 char values at s[0][0] .. s[4][4] (or at s[0] .. s[24]),
sizeof(s) == 25.
char *s[5]; --
s points to an area of allocated memory (heap if global, stack if local),
you may safely write up to 5 pointer values at s[0] .. s[4],
sizeof(s) == 40 (*).
char (*s)[5]; --
s does not point to any allocated memory -- it's merely an uninitialized pointer at this point,
you may safely write one pointer value at &s,
sizeof(s) == 8 (*).
(*) note: assuming a 64-bit architecture.
1.char s[5][5];
Here s is two dimensional array with 5 rows and 5 columns. Where in this 5 rows and 5 columns you will save element of type character.
2.char *s[5];
s is a one dimensional array with 5 elements each element is of type pointer to character.
3.char (*s)[5];
s is a pointer here not array. S points to a array of characters. for eg.
char arr[5][5];
char(*s)[5];
s = arr;
s[0][0] will be same as array of arr[0][0]
Sanity-check questions:
I did a bit of googling and discovered the correct way to return a one-dimensional integer array in C is
int * function(args);
If I did this, the function would return a pointer, right? And if the return value is r, I could find the nth element of the array by typing r[n]?
If I had the function return the number "3", would that be interpreted as a pointer to the address "3?"
Say my function was something like
int * function(int * a);
Would this be a legal function body?
int * b;
b = a;
return b;
Are we allowed to just assign arrays to other arrays like that?
If pointers and arrays are actually the same thing, can I just declare a pointer without specifying the size of the array? I feel like
int a[10];
conveys more information than
int * a;
but aren't they both ways of declaring an array? If I use the latter declaration, can I assign values to a[10000000]?
Main question:
How can I return a two-dimensional array in C? I don't think I could just return a pointer to the start of the array, because I don't know what dimensions the array has.
Thanks for all your help!
Yes
Yes but it would require a cast: return (int *)3;
Yes but you are not assigning an array to another array, you are assigning a pointer to a pointer.
Pointers and arrays are not the same thing. int a[10] reserves space for ten ints. int *a is an uninitialized variable pointing to who knows what. Accessing a[10000000] will most likely crash your program as you are trying to access memory you don't have access to or doesn't exist.
To return a 2d array return a pointer-to-pointer: int ** f() {}
Yes; array indexing is done in terms of pointer arithmetic: a[i] is defined as *(a + i); we find the address of the i'th element after a and dereference the result. So a could be declared as either a pointer or an array.
It would be interpreted as an address, yes (most likely an invalid address). You would need to cast the literal 3 as a pointer, because values of type int and int * are not compatible.
Yes, it would be legal. Pointless, but legal.
Pointers and arrays are not the same thing; in most circumstances, an expression of array type will be converted ("decay") to an expression of pointer type and its value will be the address of the first element of the array. Declaring a pointer by itself is not sufficient, because unless you initialize it to point to a block of memory (either the result of a malloc call or another array) its value will be indeterminate, and may not point to valid memory.
You really don't want to return arrays; remember that an array expression is converted to a pointer expression, so you're returning the address of the first element. However, when the function exits, that array no longer exists and the pointer value is no longer valid. It's better to pass the array you want to modify as an argument to the function, such as
void foo (int *a, size_t asize)
{
size_t i;
for (i = 0; i < asize; i++)
a[i] = some_value();
}
Pointers contain no metadata about the number of elements they point to, so you must pass that as a separate parameter.
For a 2D array, you'd do something like
void foo(size_t rows, size_t columns, int (*a)[columns])
{
size_t i, j;
for (i = 0; i < rows; i++)
for (j = 0; j < columns; j++)
a[i][j] = some_value;
}
This assumes you're using a C99 compiler or a C2011 compiler that supports variable length arrays; otherwise the number of columns must be a constant expression (i.e., known at compile time).
These answers certainly call for a bit more depth. The better you understand pointers, the less bad code you will write.
An array and a pointer are not the same, EXCEPT when they are. Off the top of my head:
int a[2][2] = { 1, 2, 3, 4 };
int (* p)[2] = a;
ASSERT (p[1][1] == a[1][1]);
Array "a" functions exactly the same way as pointer "p." And the compiler knows just as much from each, specifically an address, and how to calculate indexed addresses. But note that array a can't take on new values at run time, whereas p can. So the "pointer" aspect of a is gone by the time the program runs, and only the array is left. Conversely, p itself is only a pointer, it can point to anything or nothing at run time.
Note that the syntax for the pointer declaration is complicated. (That is why I came to stackoverflow in the first place today.) But the need is simple. You need to tell the compiler how to calculate addresses for elements past the first column. (I'm using "column" for the rightmost index.) In this case, we might assume it needs to increment the address ((2*1) + 1) to index [1][1].
However, there are a couple of more things the compiler knows (hopefully), that you might not.
The compiler knows two things: 1) whether the elements are stored sequentially in memory, and 2) whether there really are additional arrays of pointers, or just one pointer/address to the start of the array.
In general, a compile time array is stored sequentially, regardless of dimension(s), with no extra pointers. But to be sure, check the compiler documentation. Thus if the compiler allows you to index a[0][2] it is actually a[1][0], etc. A run time array is however you make it. You can make one dimensional arrays of whatever length you choose, and put their addresses into other arrays, also of whatever length you choose.
And, of course, one reason to muck with any of these is because you are choosing from using run time multiplies, or shifts, or pointer dereferences to index the array. If pointer dereferences are the cheapest, you might need to make arrays of pointers so there is no need to do arithmetic to calculate row addresses. One downside is it requires memory to store the addtional pointers. And note that if the column length is a power of two, the address can be calculated with a shift instead of a multiply. So this might be a good reason to pad the length up--and the compiler could, at least theoretically, do this without telling you! And it might depend on whether you select optimization for speed or space.
Any architecture that is described as "modern" and "powerful" probably does multiplies as fast as dereferences, and these issues go away completely--except for whether your code is correct.
If I write
int *columns[32];
am I defining an array with 32 pointers to ints?
Or is it a pointer to an array of 32 ints?
How do I differentiate between the two? Is there a difference?
Expanding on a comment to another answer:
There's a fairly straightforward procedure for reading C declarations. Start with the leftmost identifier in the declarator and work your way out, remembering that [] and () bind before *. Given the declaration
int *columns[32];
break it down as
columns -- columns
columns[32] -- is a 32-element array
*columns[32] -- of pointers
int *columns[32] -- to int.
If the declaration had been
int (*columns)[32];
then it would break down as
columns -- columns
(*columns) -- is a pointer
(*columns)[32] -- to a 32-element array
int (*columns)[32] -- of int.
This will also help you build up complex declarations. Suppose you wanted to declare an array of pointers to functions returning pointers to arrays of char:
f -- f
f[N] -- is an N-element array
*f[N] -- of pointers
(*f[N])() -- to functions
*(*f[N])() -- returning pointers
(*(*f[N])())[M] -- to M-element arrays
*(*(*f[N])())[M] -- of pointers
char *(*(*f[N])())[M]; -- to char
cdecl is a nice tool, but after you'd done this exercise a few times, you shouldn't need it.
When in doubt - ask cdecl
$> cdecl
Type `help' or `?' for help
cdecl> explain int *columns[32]
declare columns as array 32 of pointer to int
EDIT In response to comments: I found cdecl source on Google Code Search. It requires GNU readline library. I think it shouldn't be a problem to compile it on Mac OS X or Windows.
You are defining an array of 32 pointers.
To define a pointer to an array of 32 ints you have to do
int (*columns)[32];
The former declaration instantiates an array with space for 32 * sizeof(int). On the other hand, the latter instantiates a single uninitialized pointer which you can then use as follows:
int myintegers[32] = {0, 1, 2, ..., 31};
int (*columns)[32];
columns = &myintegers;
printf("%d\n", (*columns)[2]);
I hope I made the difference a little bit clear.
It is an array of 32 pointers to int and yes it does matter.
The C grammar rules specify that array access ([]) binds tighter than dereference (*) and declarations mirror usage.
The declaration int *columns[32]; means that the expression *columns[n] (where n is a number between 0 and 31) is an int. This expression is the same as *(columns[n]). The declaration allocates the space for 32 pointers, but there are no ints allocated and (assuming that this is a function local declaration) none of the pointers are initialized.
Had the declaration been int (*columns)[32]; then the expression (*columns)[n] would have been an int, meaning that the * dereference happens before the array access, so columns would have been a pointer to an array of 32 ints. The declaration would have allocated one pointer, but no arrays of ints.
A test program is illustrative, especially to those of us who aren't language lawyers:
$ gcc -x c -
#include <stdio.h>
int main(void)
{
int *columns[32];
printf("%lu\n%lu\n", sizeof(columns), sizeof(columns[0]));
return 0;
}
$ ./a.out
128
4
$
It appears to be an array of pointers.
Here are some fun declarations for you:
int *arrayOfIntP[32];
int (*pointerToArrayOf32)[32];
For more fun with multidimensional arrays look at these posts:
Array of pointers to multidimensional arrays
How do I declare a 2d array using new?
One trick is to read from right to left.
Given int* cols[32];
You first see cols[32] : this is an array of size 32.
Next you see int*: this is a pointer
All that left of array is the type of the elements in the array. So we read it as array of pointers to int (and of count 32).
Refer to Question #5 of A 'C' Test: The 0x10 Best Questions for Would-be Embedded Programmers by Nigel Jones*
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
*Alas, the original article on embedded.com can no longer be found on their website.
Can someone explain what this means?
int (*data[2])[2];
What are the parentheses for?
In C brackets [] have a higher precedence than the asterisk *
Good explanation from Wikipedia:
To declare a variable as being a
pointer to an array, we must make use
of parentheses. This is because in C
brackets ([]) have higher precedence
than the asterisk (*). So if we wish to declare a pointer to an array, we need to supply parentheses to override this:
double (*elephant)[20];
This declares that elephant is a
pointer, and the type it points at is
an array of 20 double values.
To declare a pointer to an array of
pointers, simply combine the
notations.
int *(*crocodile)[15];
Source.
And your actual case:
int (*data[2])[5];
data is an array of 2 elements. Each element contains a pointer to an array of 5 ints.
So you you could have in code using your 'data' type:
int (*data[2])[5];
int x1[5];
data[0] = &x1;
data[1] = &x1;
data[2] = &x1;//<--- out of bounds, crash data has no 3rd element
int y1[10];
data[0] = &y1;//<--- compiling error, each element of data must point to an int[5] not an int[10]
There is a very cool program called "cdecl" that you can download for Linux/Unix and probably for Windows as well. You paste in a C (or C++ if you use c++decl) variable declaration and it spells it out in simple words.
If you know how to read expressions in C, then you're one step away from reading complicated declarations.
What does
char *p;
really mean? It means that *p is a char. What does
int (*data[2])[5];
mean? It means that (*data[x])[y] is an int (provided 0 <= x < 2 and 0 <= y < 5). Now, just think about what the implications of that are. data has to be... an array of 2... pointers... to arrays of 5... integers.
Don't you think that's quite elegant? All you're doing is stating the type of an expression. Once you grasp that, declarations will never intimidate you again!
The "quick rule" is to start with the variable name, scan to the right until you hit a ), go back to the variable name and scan to the left until you hit a (. Then "step out" of the pair of parentheses, and repeat the process.
Let's apply it to something ridiculous:
void **(*(*weird)[6])(char, int);
weird is a pointer to an array of 6 pointers to functions each accepting a char and an int as argument, and each returning a pointer to a pointer to void.
Now that you know what it is and how it's done... don't do it. Use typedefs to break your declarations into more manageable chunks. E.g.
typedef void **(*sillyFunction)(char, int);
sillyFunction (*weird)[6];
data[2] - an array of two integers
*data[2] - a pointer to an array of two integers
(*data[2]) - "
(*data[2])[2] - an array of 2 pointers to arrays of two integers.
If you have an array:
int myArray[5];
int * myArrayPtr = myArray;
Would be perfectly reasonable. myArray without the brackets is a pointer to an int. When you add the brackets its like you deference the pointer myArray. You could write...
myArrayPtr[1] = 3;
Which is perfectly reasonable. Using parenthesis just makes things harder to read and understand IMO. And they show that people don't understand pointers, arrays, and pointer arithmetic, which is how the compiler or linker goes from one to the other. It seems with parenthesis you do get bounds checking though.