Reputation: 1741
I'm studying a little of C++ and I'm fighting with pointers. I understand that I can have 3 level of pointers by declaring:
int *(*x)[5];
so that *x
is a pointer to an array of 5 elements that are pointers to int
.
Also I know that x[0] = *(x+0);
, x[1] = *(x+1)
and so on....
So, given the above declaration, why is x[0] != x[0][0] != x[0][0][0]
?
Upvotes: 149
Views: 15492
Reputation: 4687
The type of int *(*x)[5]
is int* (*)[5]
i.e. a pointer to an array of 5 pointers to ints.
x
is the address of the first array of 5 pointers to ints (an address with type int* (*)[5]
)x[0]
the address of the first array of 5 pointers to ints (same address with type int* [5]
)(offset address x by 0*sizeof(int* [5])
i.e. index*size-of-type-being-pointed-to and dereference)x[0][0]
is the first pointer to an int in the array (the same address with type int*
) (offset address x by 0*sizeof(int* [5])
and dereference and then by 0*sizeof(int*)
and dereference) x[0][0][0]
is the first int being pointed to by the pointer to an int (offset address x by 0*sizeof(int* [5])
and dereference and offset that address by 0*sizeof(int*)
and dereference and offset that address by 0*sizeof(int)
and dereference) The type of int *(*y)[5][5][5]
is int* (*)[5][5][5]
i.e. a pointer to a 3d array of 5x5x5 pointers to ints
x
is the address of the first 3d array of 5x5x5 pointers to ints with type int*(*)[5][5][5]
x[0]
is the address of the first 3d array of 5x5x5 pointers to ints (offset address x by 0*sizeof(int* [5][5][5])
and dereference)x[0][0]
is the address of the first 2d array of 5x5 pointers to ints (offset address x by 0*sizeof(int* [5][5][5])
and dereference then offset that address by 0*sizeof(int* [5][5])
)x[0][0][0]
is the address of the first array of 5 pointers to ints (offset address x by 0*sizeof(int* [5][5][5])
and dereference and offset that address by 0*sizeof(int* [5][5])
and offset that address by 0*sizeof(int* [5])
)x[0][0][0][0]
is the first pointer to an int in the array (offset address x by 0*sizeof(int* [5][5][5])
and dereference and offset that address by 0*sizeof(int* [5][5])
and offset that address by 0*sizeof(int* [5])
and offset that address by 0*sizeof(int*)
and dereference)x[0][0][0][0][0]
is the first int being pointed to by the pointer to an int (offset address x by 0*sizeof(int* [5][5][5])
and dereference and offset that address by 0*sizeof(int* [5][5])
and offset that address by 0*sizeof(int* [5])
and offset that address by 0*sizeof(int*)
and dereference and offset that address by 0*sizeof(int)
and dereference) As for array decay:
void function (int* x[5][5][5]){
printf("%p",&x[0][0][0][0]); //get the address of the first int pointed to by the 3d array
}
This is equivalent to passing int* x[][5][5]
or int* (*x)[5][5]
i.e. they all decay to the latter. This is why you wont get a compiler warning for using x[6][0][0]
in the function but you will for x[0][6][0]
because that size information is preserved
void function (int* (*x)[5][5][5]){
printf("%p",&x[0][0][0][0][0]); //get the address of the first int pointed to by the 3d array
}
x[0]
is the address of the first 3d array of 5x5x5 pointers to intsx[0][0]
is the address of the first 2d array of 5x5 pointers to intsx[0][0][0]
is the address of the first array of 5 pointers to intsx[0][0][0][0]
is the first pointer to an int in the arrayx[0][0][0][0][0]
is the first int being pointed to by the pointer to an intIn the last example, it is semantically much clearer to use *(*x)[0][0][0]
rather than x[0][0][0][0][0]
, this is because the first and last [0]
here are
interpreted as a pointer dereference rather than an index into a multidimensional array, because of the type. They are however identical because (*x) == x[0]
regardless of the semantics. You could also use *****x
, which would look like you're dereferencing the pointer 5 times, but it is actually interpreted exactly the same: an offset, a dereference, a dereference, 2 offsets into an array and a dereference, purely because of the type you are applying the operation to.
Essentially when you [0]
or *
a *
to a non array type, it is an offset and a dereference due to the order of precedence of *(a + 0)
.
When you [0]
or *
a *
to an array type then it's an offset then an idempotent dereference (the dereference is resolved by the compiler to yield the same address – it's an idempotent operation).
When you [0]
or *
a type with a 1d array type then it's an offset then a dereference
If you [0]
or **
a 2d array type then it's an offset only i.e. an offset and then an idempotent dereference.
If you [0][0][0]
or ***
a 3d array type then it's an offset + idempotent dereference then an offset + idempotent dereference then an offset + idempotent dereference then a dereference. The true dereference only occurs when the array type is fully stripped off.
For the example of int* (*x)[1][2][3]
the type is unwrapped in order.
x
has a type int* (*)[1][2][3]
*x
has a type int* [1][2][3]
(offset 0 + idempotent dereference) **x
has a type int* [2][3]
(offset 0 + idempotent dereference) ***x
has a type int* [3]
(offset 0 + idempotent dereference) ****x
has a type int*
(offset 0 + dereference) *****x
has type int
(offset 0 + dereference) Upvotes: 0
Reputation: 11434
x[0] != x[0][0] != x[0][0][0]
is, according to your own post,
*(x+0) != *(*(x+0)+0) != *(*(*(x+0)+0)+0)`
which is simplified
*x != **x != ***x
Why should it be equal?
The first one is the address of some pointer.
The second one is the address of another pointer.
And the third one is some int
value.
Upvotes: 132
Reputation: 17071
The other answers are correct, but none of them emphasize the idea that it is possible for all three to contain the same value, and so they're in some way incomplete.
The reason this can't be understood from the other answers is that all the illustrations, while helpful and definitely reasonable under most circumstances, fail to cover the situation where the pointer x
points to itself.
This is pretty easy to construct, but clearly a bit harder to understand. In the program below, we'll see how we can force all three values to be identical.
NOTE: The behavior in this program is undefined, but I'm posting it here purely as an interesting demonstration of something that pointers can do, but shouldn't.
#include <stdio.h>
int main () {
int *(*x)[5];
x = (int *(*)[5]) &x;
printf("%p\n", x[0]);
printf("%p\n", x[0][0]);
printf("%p\n", x[0][0][0]);
}
This compiles without warnings in both C89 and C99, and the output is the following:
$ ./ptrs
0xbfd9198c
0xbfd9198c
0xbfd9198c
Interestingly enough, all three values are identical. But this shouldn't be a surprise! First, let's break down the program.
We declare x
as a pointer to an array of 5 elements where each element is of type pointer to int. This declaration allocates 4 bytes on the runtime stack (or more depending on your implementation; on my machine pointers are 4 bytes), so x
is referring to an actual memory location. In the C family of languages, the contents of x
are just garbage, something left over from previous usage of the location, so x
itself doesn't point anywhere—certainly not to allocated space.
So, naturally, we can take the address of the variable x
and put it somewhere, so that's exactly what we do. But we'll go ahead and put it into x itself. Since &x
has a different type than x
, we need to do a cast so we don't get warnings.
The memory model would look something like this:
0xbfd9198c
+------------+
| 0xbfd9198c |
+------------+
So the 4-byte block of memory at the address 0xbfd9198c
contains the bit pattern corresponding to the hexadecimal value 0xbfd9198c
. Simple enough.
Next, we print out the three values. The other answers explain what each expression refers to, so the relationship should be clear now.
We can see that the values are the same, but only in a very low level sense...their bit patterns are identical, but the type data associated with each expression means their interpreted values are different.
For instance, if we printed out x[0][0][0]
using the format string %d
, we'd get a huge negative number, so the "values" are, in practice, different, but the bit pattern is the same.
This is actually really simple...in the diagrams, the arrows just point to the same memory address rather than to different ones. However, while we were able to force an expected result out of undefined behavior, it is just that—undefined. This isn't production code but simply a demonstration for the sake of completeness.
In a reasonable situation, you will use malloc
to create the array of 5 int pointers, and again to create the ints that are pointed to in that array. malloc
always returns a unique address (unless you're out of memory, in which case it returns NULL or 0), so you'll never have to worry about self-referential pointers like this.
Hopefully that's the complete answer you're looking for. You shouldn't expect x[0]
, x[0][0]
, and x[0][0][0]
to be equal, but they could be if forced. If anything went over your head, let me know so I can clarify!
Upvotes: 1
Reputation: 2999
Being p
a pointer: you're stacking dereferences with p[0][0]
, which is equivalent to *((*(p+0))+0)
.
In C reference (&) and dereference (*) notation:
p == &p[0] == &(&p[0])[0] == &(&(&p[0])[0])[0])
Is equivalent to:
p == &*(p+0) == &*(&*(p+0))+0 == &*(&*(&*(p+0))+0)+0
Look that, the &* can be refactored, just removing it:
p == p+0 == p+0+0 == p+0+0+0 == (((((p+0)+0)+0)+0)+0)
Upvotes: 2
Reputation: 3784
There's a principle in C++ so that: a declaration of a variable indicates exactly the way of using the variable. Consider your declaration:
int *(*x)[5];
that can be rewritten as (for clearer):
int *((*x)[5]);
Due to the principle, we have:
*((*x)[i]) is treated as an int value (i = 0..4)
→ (*x)[i] is treated as an int* pointer (i = 0..4)
→ *x is treated as an int** pointer
→ x is treated as an int*** pointer
Therefore:
x[0] is an int** pointer
→ x[0][0] = (x[0]) [0] is an int* pointer
→ x[0][0][0] = (x[0][0]) [0] is an int value
So you can figure out the difference.
Upvotes: 4
Reputation: 1666
x[0]
dereferences the outermost pointer (pointer to array of size 5 of pointer to int) and results in an array of size 5 of pointer to int
;x[0][0]
dereferences the outermost pointer and indexes the array, resulting in a pointer to int
;x[0][0][0]
dereferences everything, resulting in a concrete value.By the way, if you ever feel confused by what these kind of declarations mean, use cdecl.
Upvotes: 18
Reputation: 71
If you were to view the arrays from a real-world perspective, it would appear as thus:
x[0]
is a freight container full of crates.
x[0][0]
is a single crate, full of shoeboxes, within the freight container.
x[0][0][0]
is a single shoebox inside the crate, inside the freight container.
Even if it were the only shoebox in the only crate in the freight container, it is still a shoebox and not a freight container
Upvotes: 7
Reputation: 106102
x
is a pointer to an array of 5 pointers to int
.
x[0]
is an array of 5 pointers to int
.
x[0][0]
is a pointer to an int
.
x[0][0][0]
is an int
.
x[0]
Pointer to array +------+ x[0][0][0]
x -----------------> | | Pointer to int +-------+
0x500 | 0x100| x[0][0]----------------> 0x100 | 10 |
x is a pointer to | | +-------+
an array of 5 +------+
pointers to int | | Pointer to int
0x504 | 0x222| x[0][1]----------------> 0x222
| |
+------+
| | Pointer to int
0x508 | 0x001| x[0][2]----------------> 0x001
| |
+------+
| | Pointer to int
0x50C | 0x123| x[0][3]----------------> 0x123
| |
+------+
| | Pointer to int
0x510 | 0x000| x[0][4]----------------> 0x000
| |
+------+
You can see that
x[0]
is an array and will converted to pointer to its first element when used in an expression (with some exceptions). Therefore x[0]
will give the address of its first element x[0][0]
which is 0x500
. x[0][0]
contains address of an int
which is 0x100
. x[0][0][0]
contains an int
value of 10
. So, x[0]
is equal to &x[0][0]
and therefore, &x[0][0] != x[0][0]
.
Hence, x[0] != x[0][0] != x[0][0][0]
.
Upvotes: 261
Reputation: 40655
Here is the memory layout of your pointer:
+------------------+
x: | address of array |
+------------------+
|
V
+-----------+-----------+-----------+-----------+-----------+
| pointer 0 | pointer 1 | pointer 2 | pointer 3 | pointer 4 |
+-----------+-----------+-----------+-----------+-----------+
|
V
+--------------+
| some integer |
+--------------+
x[0]
yields "address of array",
x[0][0]
yields "pointer 0",
x[0][0][0]
yields "some integer".
I believe, it should be obvious now, why they are all different.
The above is close enough for basic understanding, which is why I wrote it the way I wrote it. However, as haccks rightly points out, the first line is not 100% precise. So here come all the fine details:
From the definition of the C language, the value of x[0]
is the whole array of integer pointers. However, arrays are something you can't really do anything with in C. You always manipulate either their address or their elements, never the entire array as a whole:
You can pass x[0]
to the sizeof
operator. But that's not really a use of the value, its result depends of the type only.
You can take its address which yields the value of x
, i. e. "address of array" with the type int*(*)[5]
. In other words: &x[0] <=> &*(x + 0) <=> (x + 0) <=> x
In all other contexts, the value of x[0]
will decay into a pointer to the first element in the array. That is, a pointer with the value "address of array" and the type int**
. The effect is the same as if you had casted x
to a pointer of type int**
.
Due to the array-pointer decay in case 3., all uses of x[0]
ultimately result in a pointer that points the beginning of the pointer array; the call printf("%p", x[0])
will print the contents of the memory cells labeled as "address of array".
Upvotes: 50
Reputation: 535
First thing I have to say that
x [ 0 ] = * ( x + 0 ) = * x ;
x [ 0 ] [ 0 ] = * ( * ( x + 0 ) + 0 ) = * * x ;
x [ 0 ] [ 0 ] [ 0 ] = * ( * ( * ( x + 0 ) + 0 ) ) = * * * x ;
So * x ≠ * * x ≠ * * * x
From the following picture all things are clear.
x[0][0][0]= 2000
x[0][0] = 1001
x[0] = 10
It is just an example, where value of x[0][0][0]=10
and address of x[0][0][0] is 1001
that address is stored in x[0][0]=1001
and address of x[0][0] is 2000
and that address is stored at x[0]=2000
So x[0][0][0] ≠ x[0][0] ≠ x[0]
.
EDITINGS
Program 1:
{
int ***x;
x=(int***)malloc(sizeof(int***));
*x=(int**)malloc(sizeof(int**));
**x=(int*)malloc(sizeof(int*));
***x=10;
printf("%d %d %d %d\n",x,*x,**x,***x);
printf("%d %d %d %d %d",x[0][0][0],x[0][0],x[0],x,&x);
}
Output
142041096 142041112 142041128 10
10 142041128 142041112 142041096 -1076392836
Program 2:
{
int x[1][1][1]={10};
printf("%d %d %d %d \n ",x[0][0][0],x[0][0],x[0],&x);
}
Output
10 -1074058436 -1074058436 -1074058436
Upvotes: 10
Reputation: 311088
Let consider step by step expressions x[0]
, x[0][0]
and x[0][0][0]
.
As x
is defined the following way
int *(*x)[5];
then expression x[0]
is an array of type int *[5]
. Take into account that expression x[0]
is equivalent to expression *x
. That is dereferencing a pointer to an array we get the array itself. Let denote it like y that is we have a declaration
int * y[5];
Expression x[0][0]
is equivalent to y[0]
and has type int *
. Let denote it like z that is we have a declaration
int *z;
expression x[0][0][0]
is equivalent to expression y[0][0]
that in turn is equivalent to expression z[0]
and has type int
.
So we have
x[0]
has type int *[5]
x[0][0]
has type int *
x[0][0][0]
has type int
So they are objects of different types and by the way of different sizes.
Run for example
std::cout << sizeof( x[0] ) << std::endl;
std::cout << sizeof( x[0][0] ) << std::endl;
std::cout << sizeof( x[0][0][0] ) << std::endl;
Upvotes: 11
Reputation: 10343
You are trying to compare different types by value
If you take the addresses you might get more of what you expect
Keep in mind that your declaration makes a difference
int y [5][5][5];
would allow the comparisons you want, since y
, y[0]
, y[0][0]
, y[0][0][0]
would have different values and types but the same address
int **x[5];
does not occupy contiguous space.
x
and x [0]
have the same address, but x[0][0]
and x[0][0][0]
are each at different addresses
Upvotes: 2