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.
Related
EDIT: the correct syntax for declaring a function with the return type of array pointer in the example I wrote further below in my code example section should be as follows:
int (*my_function(void))[10]
{
...
}
Important note for future readers who might come across the same question: don't typecast malloc! Just don't. Thanks to user3386109 for that one. Further reading on the why.
Thanks to Andrew Henle and Eric Postpischil for their answers.
Here's a really good explanation if anyone wants to read further on the the topic.
Original thread below:
What I want to know
How to return a pointer to array in C.
Why I want to know that
Because it (is?) should be possible to do so.
For what I want it
To return multidimensional arrays from functions.
"Have you tried structs?"
Yes, but this isn't about structs. The point (no pun intended) of this thread is doing it with pointers.
Code example
My example is wrong, thus the reason I'm asking here, but here it go:
int (*)[10]my_function(void) // Should be returning a type of array pointer to 10-elements array. Sure enough it's not.
{
int (*ptr)[10] = (int (*)[10]) malloc(10 * sizeof(int));
return ptr;
}
int main(void)
{
printf("Address of ptr is: %d", my_function());
return 0;
}
As you can see, I'm not sure about how to typecast malloc to type array pointer to 10-elements array as well. If you know how, please let me know.
IMPORTANT NOTE
As far as I know, using double pointers (pointer to pointer) in this case is wrong.
Code example:
int **my_function(void); // WRONG, afaik
Rationale: this video, at 16:22.
I already tried searching online and in some books, they have examples using structs, pointer to array as function arguments, but not this.
If you have some knowledge about this topic, please reply, I'll be grateful.
Arrays can't be passed by value, unless you embed them in a structure. When you pass an array to or from a function, it decays to a pointer to the first element. So the way to return a dynamically-allocated array is as a single pointer.
int *my_function(void) {
int *ptr = malloc(10 * sizeof(int));
return ptr;
}
There's no need to specify the dimension in the type, as this will be ignored.
This is not a multi-dimensional array, it's just a 1-d array of 10 int.
The type declaration int (*ptr)[10] is for an array of 10 pointers to integers, not a pointer to an array of 10 integers.
int (*p)[10] declares a pointer to an array of 10 int.
int (*)[10] is a type that is a pointer to an array of 10 int. However, note that you do not need to cast the result of malloc in C. As a void * used as an initializer or right operand of assignment, it will be automatically converted to the target type.
int (*foo(void))[10] declares a function taking no parameters that returns a pointer to an array of 10 int.
Note that int (*foo(void))[10] can be derived from int (*p)[10] simply by replacing p with foo(void). Generally, to get any return type for a function, write a declaration for an object of that type, then replace the object name with the function name followed by its usual parameter declarations.
This question already has answers here:
Is 2d array a double pointer? [duplicate]
(4 answers)
Closed 8 years ago.
What I learnt from C language is that
int **matrix = matrix is a pointer to pointer to int
when we want to create a matrix we will malloc a set of contigus pointers !
so here is the first pointer pointing to 1 pointer to an int or it can point to a set of pointers which are pointing to an int ( the first pointer will of course point to the address of the first pointer )
Brievely pointing to 1 pointer (only one ) is it the same as pointing to first pointer from a set of pointers ?
I think the answer resides inside this question What is exactly an array of something ?
Pointers and arrays are fundamentally different, especially wrt. to their behavior with sizeof() and wrt. what they allocate. However, they can sometimes be used interchangeably, because both essentially represent an address.
If you dynamically allocate memory, you will receive a pointer, which points to the beginning of a chunk of memory. If you allocate memory of a size that represents a multiple of a type's size, it should be intuitively clear that you can store as many elements of one type there, as you have allocated space for. Since C pointer arithmetic interprets *(p + n) - which is the same as p[n] and n[p] - as an access to the address of p plus n times the size of an element of the type p points to, it should now be easier to understand that you can interpret the pointer as the beginning of an array.
For your case, that means you can interpret int **p as a pointer to an int-pointer. Past the memory of this pointer, n more int pointers may follow, while every one of these pointers represents the address of an int, past which, once again, n more ints may follow. Thus, even though int **p is actually a pointer to a pointer of type int, it is possible to interpret it as two-dimensional array. How many elements past your pointer belong to the array is something that you cannot know from neither an array not a pointer, which is why you will usually have a n_size and/or m_size argument or something similar.
In the beginning I said that you can "sometimes" treat them as the same. To make this explicit, you can use pointers and arrays interchangeably in these cases:
When passed to a function a type[] "decays" to a type * e.g. f(type *a)
If accessing elements with the [] operator, a[i] always resolves to *(a + i)
When they occur in an expression, e.g. ++a
cf: van der Linden - Deep C
if you want a quick answer try making this tiny program
#include <stdio.h>
#include <stdlib.h>
int main()
{
int matrix[2][3];
int **m;
m=malloc(sizeof(int *)*2);
m[0]=malloc(sizeof(int)*3);
m[1]=malloc(sizeof(int)*3);
m=matrix;
return 0;
}
The compiler will answer you that the two declarations are different. In fact:
This question already has answers here:
Should I use char** argv or char* argv[]?
(10 answers)
Closed 8 years ago.
I've been fiddling around to see if there's any way to retain information about an array's length automatically when passed into a function (see my other question here: Why is this array size "workaround" giving me a warning?), but my question is more about a warning that gcc is giving that doesn't make sense to me.
According to this website (EDIT: I misread the website), char *p[10] declares a pointer to a 10-wide array of chars. But when I tried to pass in a pointer to an array into a function, I got this error message from the compiler:
Here is the rest of the program:
I know that when an array is passed into a function, it decays into a pointer (losing information about its length), but it seems that the declaration itself is decaying. What's going on here?
EDIT: When I replace the char *p[10] with char (*p)[10], it doesn't give the warning anymore, and more importantly, it displays the proper array length: 10. I guess my questions are 1) Why do the parentheses change things? and 2) Is this a well-known workaround or am I relying on some behavior of the compiler that isn't guaranteed? (i.e. that array length info can be passed by indirectly passing in a pointer to it?)
In fact char *p[10] is an array, of length 10, of pointers to char. You are looking for char (*p)[10]. That is a pointer to an array, of length 10, of char.
You might find http://cdecl.org/ a useful resource to help you test your understanding of declarations.
Regarding the discussion surrounding dynamic arrays, you are going to have to accept that once you allocate an array dynamically, the system provides no means for you to recover the length of the array. It is your responsibility to remember that information.
The subject of your question has been answered already but I wanted to address the heart of it, which is "can I encode the length of an array in its type?" Which is in fact what a pointer-to-array does. The real question is whether you can actually gain any brevity or safety from this. Consider that in each scope where you have a declaration of your type, the length still needs to be known a-priori. To show you what I mean let's generalize your example slightly by making 10 a compile-time constant N.
#define N 10
size_t arraylength(char (*arrayp)[N]) {
return sizeof(*arrayp);
}
int main(void) {
char array[N];
assert( arraylength(&array) == N ); //always true
}
So far so good. We didn't have to pass the length of array anywhere. But it's easy to see that anywhere the expression sizeof(*arrayp) is used, we also could have written N. And any place we declare a char(*)[ ], the bracketed length must come from somewhere.
So what if N isn't a compile time constant, and array is either a VLA or a pointer-to-array from malloc? We can still write and call arraysize, but it looks like this:
size_t arraylength(size_t N, char (*arrayp)[N]) {
return sizeof(*arrayp);
}
int main(void) {
size_t N = length_from_somewhere();
char array[N];
assert( arraylength(sizeof(array), &array) == N );
}
In defining arraysize N must still be visible before the declaration of arrayp. In either case, we can't avoid having N visible outside of the declaration of arrayp. In fact, we didn't gain anything over writing arraysize(size_t N, char* array) and passing array directly (which is a bit silly given the purpose of this function.) Both times arraylength could have equally been written return N;
Which isn't to say that array pointers are useless as parameters to functions -- in the opposite situation, when you want to enforce a length, they can provide type checking to make sure somefunc(char (*)[10]); receives a pointer to an array that is really (sans shady casting) 10 elements long, which is stronger than what a construct like [static 10] provides.
Also keep in mind that all of the length measurements above depend on the underlying type being char where length == size. For any larger type, taking the length requires the usual arithmetic e.g.
sizeof(*arrayp)/sizeof((*arrayp)[0])
In C, arrays decay to pointers to their first elements on most uses. In particular, what a function receives is always just a pointer to the first element, the size of the array is not passed with it.
Get a good text on C and read up on arrays.
I've been fiddling around to see if there's any way to retain information about an array's length automatically when passed into a function
The problem is so annoying that lots of programmers would love to have an answer. Unfortunately, this is not possible.
It seems that the declaration itself is decaying
Pointer to an array is not the same as a pointer to a pointer; that is why you are getting an error.
There is no decaying going on in your code, because you are not passing an array in your code sample: instead, you are trying to pass a pointer to an array &p. The pointer to an array of characters is not compatible to the expected type of the function, which is char**. Array size from the declaration is ignored.
You need to keep in mind two things:
1. Arrays are not pointers.
2. Array names decays to pointers (in most cases) when passed as arguments to functions.
So, when you declare
int a[10]; // a is an array of 10 ints
int *b; // b is a pointer to int
both of a and b are of different types. Former is of type int [10] while latter is of type int *.
In case of function parameter
void foo1 (int a[10]); // Actually you are not passing entire array
void foo2 (int a[]); // And that's why you can omit the first dimension.
void foo3 (int *a); // and the compiler interprets the above two third
ain all of the above function declarations is of same data type int *.
Now in your case
unsigned long arraySize(char *p[10]);
you can declare it as
unsigned long arraySize(char *p[]);
and hence
unsigned long arraySize(char **p);
All are equivalent.
char *p[10] char *p[] and char **p all are exactly equivalent but when they are declared as parameter of a function otherwise char *p[10] (an array of 10 pointers to char) and char **p (a pointer to pointer to char)are entirely of different type.
Suggested reading: C-FAQ: 6. Arrays and Pointers explains this in detailed.
Array name itself is a constant pointer. for example int arr[10]={0};
arr contains the address of arr[0]. hence arr equals&arr[0] .
when u pass the arraysize(&p) , you are actually passing a double pointer .
The correct format to pass a array pointer would be arraysize(&p[0]) or arraysizeof(p)
Note Array name is constant pointer , you cant change its value .
int arr[10];
arr++;
is invalid.
In your case you cant find a size of an array in function by passing the array name . it would return size of pointer(4 or 8 depends on your processor .
The method is to pass the size along with the array
func(array_name , array_size);
I have an array of int pointers int* arr[MAX]; and I want to store its address in another variable. How do I define a pointer to an array of pointers? i.e.:
int* arr[MAX];
int (what here?) val = &arr;
The correct answer is:
int* arr[MAX];
int* (*pArr)[MAX] = &arr;
Or just:
int* arr [MAX];
typedef int* arr_t[MAX];
arr_t* pArr = &arr;
The last part reads as "pArr is a pointer to array of MAX elements of type pointer to int".
In C the size of array is stored in the type, not in the value. If you want this pointer to correctly handle pointer arithmetic on the arrays (in case you'd want to make a 2-D array out of those and use this pointer to iterate over it), you - often unfortunately - need to have the array size embedded in the pointer type.
Luckily, since C99 and VLAs (maybe even earlier than C99?) MAX can be specified in run-time, not compile time.
Should just be:
int* array[SIZE];
int** val = array;
There's no need to use an address-of operator on array since arrays decay into implicit pointers on the right-hand side of the assignment operator.
IIRC, arrays are implicitly convertible to pointers, so it would be:
int ** val = arr;
According to this source http://unixwiz.net/techtips/reading-cdecl.html, by using the "go right when you can, go left when you must" rule, we get the following 2 meanings of the declarations given in the previous answers -
int **val ==> val is a pointer to pointer to int
int* (*pArr)[MAX] ==> pArr is a pointer to an array of MAX length pointers to int.
I hope the above meanings make sense and if they don't, it would probably be a good idea to peruse the above mentioned source.
Now it should be clear that the second declaration is the one which moteutsch is looking for as it declares a pointer to an array of pointers.
So why does the first one also work? Remember that
int* arr[MAX]
is an array of integer pointers. So, val is a pointer to, the pointer to the first int declared inside the int pointer array.
#define SIZE 10
int *(*yy)[SIZE];//yy is a pointer to an array of SIZE number of int pointers
and so initialize yy to array as below -
int *y[SIZE]; //y is array of SIZE number of int pointers
yy = y; // Initialize
//or yy = &y; //Initialize
I believe the answer is simply:
int **val;
val = arr;
As far as I know there is no specific type "array of integers" in c, thus it's impossible to have a specific pointer to it. The only thing you can do is to use a pointer to the int: int*, but you should take into account a size of int and your array length.
I'm messing around with multidimensional arrays and pointers. I've been looking at a program that prints out the contents of, and addresses of, a simple array. Here's my array declaration:
int zippo[4][2] = { {2,4},
{6,8},
{1,3},
{5,7} };
My current understanding is that zippo is a pointer, and it can hold the address of a couple of other pointers. By default, zippo holds the address of pointer zippo[0], and it can also hold the addresses of pointers zippo[1], zippo[2], and zippo[3].
Now, take the following statement:
printf("zippo[0] = %p\n", zippo[0]);
printf(" *zippo = %p\n", *zippo);
printf(" zippo = %p\n", zippo);
On my machine, that gives the following output:
zippo[0] = 0x7fff170e2230
*zippo = 0x7fff170e2230
zippo = 0x7fff170e2230
I perfectly understand why zippo[0] and *zippo have the same value. They're both pointers, and they both store the address (by default) of the integer 2, or zippo[0][0]. But what is up with zippo also sharing the same memory address? Shouldn't zippo be storing the address of the pointer zippo[0]? Whaaaat?
When an array expression appears in most contexts, its type is implicitly converted from "N-element array of T" to "pointer to T", and its value is set to point to the first element in the array. The exceptions to this rule are when the array expression is an operand of either the sizeof or address-of (&) operators, or when the array is a string literal being used as an initializer in a declaration.
Thus, the expression zippo "decays" from type int [4][2] (4-element array of 2-element arrays of int) to int (*)[2] (pointer to 2-element array of int). Similarly, the type of zippo[0] is int [2], which is implicitly converted to int *.
Given the declaration int zippo[4][2], the following table shows the types of various array expressions involving zippo and any implicit conversions:
Expression Type Implicitly converted to Equivalent expression
---------- ---- ----------------------- ---------------------
zippo int [4][2] int (*)[2]
&zippo int (*)[4][2]
*zippo int [2] int * zippo[0]
zippo[i] int [2] int *
&zippo[i] int (*)[2]
*zippo[i] int zippo[i][0]
zippo[i][j] int
&zippo[i][j] int *
*zippo[i][j] invalid
Note that zippo, &zippo, *zippo, zippo[0], &zippo[0], and &zippo[0][0] all have the same value; they all point to the base of the array (the address of the array is the same as the address of the first element of the array). The types of the various expressions all differ, though.
When you declare a multidimensional array, the compiler treats it as a single dimensional array. Multidimensional arrays are just an abstraction to make our life easier. You have a misunderstanding: This isn't one array pointing to 4 arrays, its always just a single contigous block of memory.
In your case, doing:
int zippo[4][2]
Is really the same as doing
int zippo[8]
With the math required for the 2D addressing handled for you by the compiler.
For details, see this tutorial on Arrays in C++.
This is very different than doing:
int** zippo
or
int* zippo[4]
In this case, you're making an array of four pointers, which could be allocated to other arrays.
zippo is not a pointer. It's an array of array values. zippo, and zippo[i] for i in 0..4 can "decay" to a pointer in certain cases (particularly, in value contexts). Try printing sizeof zippo for an example of the use of zippo in a non-value context. In this case, sizeof will report the size of the array, not the size of a pointer.
The name of an array, in value contexts, decays to a pointer to its first element. So, in value context, zippo is the same as &zippo[0], and thus has the type "pointer to an array [2] of int"; *zippo, in value context is the same as &zippo[0][0], i.e., "pointer to int". They have the same value, but different types.
I recommend reading Arrays and Pointers for answering your second question. The pointers have the same "value", but point to different amounts of space. Try printing zippo+1 and *zippo+1 to see that more clearly:
#include <stdio.h>
int main(void)
{
int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5,7} };
printf("%lu\n", (unsigned long) (sizeof zippo));
printf("%p\n", (void *)(zippo+1));
printf("%p\n", (void *)(*zippo+1));
return 0;
}
For my run, it prints:
32
0xbffede7c
0xbffede78
Telling me that sizeof(int) on my machine is 4, and that the second and the third pointers are not equal in value (as expected).
Also, "%p" format specifier needs void * in *printf() functions, so you should cast your pointers to void * in your printf() calls (printf() is a variadic function, so the compiler can't do the automatic conversion for you here).
Edit: When I say an array "decays" to a pointer, I mean that the name of an array in value context is equivalent to a pointer. Thus, if I have T pt[100]; for some type T, then the name pt is of type T * in value contexts. For sizeof and unary & operators, the name pt doesn't reduce to a pointer. But you can do T *p = pt;—this is perfectly valid because in this context, pt is of type T *.
Note that this "decaying" happens only once. So, let's say we have:
int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5,7} };
Then, zippo in value context decays to a pointer of type: pointer to array[2] of int. In code:
int (*p1)[2] = zippo;
is valid, whereas
int **p2 = zippo;
will trigger an "incompatible pointer assignment" warning.
With zippo defined as above,
int (*p0)[4][2] = &zippo;
int (*p1)[2] = zippo;
int *p2 = zippo[0];
are all valid. They should print the same value when printed using printf("%p\n", (void *)name);, but the pointers are different in that they point to the whole matrix, a row, and a single integer respectively.
The important thing here is that int zippy[4][2] is not the same type of object as int **zippo.
Just like int zippi[5], zippy is the address of a block of memory. But the compiler knows that you want to address the eight memory location starting at zippy with a two dimensional syntax, but want to address the five memory location starting at zippi with a one dimensional syntax.
zippo is a different thing entirely. It holds the address of a a block of memory big enough to contain two pointer, and if you make them point at some arrays of integers, you can dereference them with the two dimensional array access syntax.
Very well explained by Reed, I shall add few more points to make it simpler, when we refer to zippo or zippo[0] or zippo[0][0], we are still referring to the same base address of the array zippo. The reason being arrays are always contiguous block of memory and multidimensional arrays are multiple single dimension arrays continuously placed.
When you have to increment by each row, you need a pointer int *p = &zippo[0][0], and doing p++ increments the pointer by every row.
In your example id its a 4 X 2 array, on doing p++ its, pointer currently points to second set of 4 elements.