Reputation: 33
I'm trying to get a grip on pointers and arrays in C. Now, I'm stuck on trying to figure out how my C compiler allocates memory for the elements in a two dimensional array. Here's my example code:
#include <stdio.h>
int main(void)
{
int ar[2][2] = { {1, 2}, {3, 4} };
printf("sizeof(int) = %u\n-----\n", sizeof(int));
printf("ar = %p\n", ar);
printf("ar + 1 = %p\n", ar + 1);
printf("&ar = %p\n", &ar);
printf("&ar + 1 = %p\n\n", &ar + 1);
printf("sizeof(ar) = %u\n-----\n", sizeof(ar));
printf("ar[0] = %p\n", ar[0]);
printf("ar[0] + 1 = %p\n", ar[0] + 1);
printf("&ar[0] = %p\n", &ar[0]);
printf("&ar[0] + 1 = %p\n\n", &ar[0] + 1);
printf("sizeof(ar[0]) = %u\n-----\n", sizeof(ar[0]));
printf("ar[1] = %p\n", ar[1]);
printf("ar[1] + 1 = %p\n", ar[1] + 1);
printf("&ar[1] = %p\n", &ar[1]);
printf("&ar[1] + 1 = %p\n\n", &ar[1] + 1);
printf("sizeof(ar[1]) = %u\n-----\n", sizeof(ar[1]));
printf("&ar[0][0] = %p\n", &ar[0][0]);
printf("&ar[0][0] + 1 = %p\n", &ar[0][0] + 1);
printf("&ar[1][0] = %p\n", &ar[1][0]);
printf("&ar[1][0] + 1 = %p\n\n", &ar[1][0] + 1);
printf("sizeof(ar[0][0]) = %u\n-----\n", sizeof(ar[0][0]));
return 0;
}
The output I get on my system is:
sizeof(int) = 4
-----
ar = 0061FF20
ar + 1 = 0061FF28
&ar = 0061FF20
&ar + 1 = 0061FF30
sizeof(ar) = 16
-----
ar[0] = 0061FF20
ar[0] + 1 = 0061FF24
&ar[0] = 0061FF20
&ar[0] + 1 = 0061FF28
sizeof(ar[0]) = 8
-----
ar[1] = 0061FF28
ar[1] + 1 = 0061FF2C
&ar[1] = 0061FF28
&ar[1] + 1 = 0061FF30
sizeof(ar[1]) = 8
-----
&ar[0][0] = 0061FF20
&ar[0][0] + 1 = 0061FF24
&ar[1][0] = 0061FF28
&ar[1][0] + 1 = 0061FF2C
sizeof(ar[0][0]) = 4
-----
I understand why ar
is 16 bytes in size; it should be able to hold 4 int
s, which on my system is 4x4 = 16 bytes. This, I guess, is also why the difference in bytes between &ar + 1
and &ar
is (hex) 30 - 20 = 16.
What I don't understand is why the difference between ar + 1
and ar
is only 8 bytes. This would mean that the array can only hold 2 int
s á 4 bytes.
I have the same problem understanding ar[0]
and ar[1]
as you can see in my code.
Shouldn't ar + 1
and &ar + 1
produce the same result?
Upvotes: 1
Views: 85
Reputation: 141698
So:
sizeof(int) == 4
The following:
int ar[2][2];
is a 2D array.
We know that, a[b]
is equal to *(a + b)
. And &*
is converted to nothing.
So:
&ar[1]
is equal to
(ar + 1)
here ar
"decays" or "shall be adjusted" (read as: magically converts) into a pointer. A pointer to an array of two int elements, ie. int (*)[2]
. So it's not an int *
nor int[2][2]
pointer but int (*)[2]
. We know that
sizeof(ar) == sizeof(int[2][2]) == sizeof(int[2]) * 2 == sizeof(int) * 2 * 2
sizeof(*ar) == sizeof(*(int(*)[2]) == sizeof(int[2]) == sizeof(int) * 2
sizeof(**ar) == sizeof(**(*(int(*)[2])) == sizeof(*(int[2])) == sizeof(*(int*)) == sizeof(int)
So
(ar + 1)
is equal to (to the value):
(uintptr_t)ar + sizeof(*ar) * 1 ==
(uintptr_t)ar + sizeof(*(int(*)[2])) * 1) ==
(uintptr_t)ar + sizeof(int[2]) * 1) ==
(uintptr_t)ar + sizeof(int) * 2 * 1)
ie. it increments the ar
pointer value by 2 * sizeof(int)
.
What I don't understand is why the difference between ar + 1 and ar is only 8 bytes.
ar + 1
is equal to
(uintptr_t)ar + sizeof(*ar) + 1
As ar
is int[2][2]
, then *ar
is int[2]
, so sizeof(*ar) = sizeof(int) * 2
.
So ar + 1
is equal to
(uintptr_t)ar + sizeof(int) * 2 * 1
So (ar + 1) - ar
is equal to
((uintptr_t)ar + sizeof(int[2]) * 1) - (uintrpt_t)ar ==
sizeof(int[2]) ==
sizeof(int) * 2
Shouldn't ar + 1 and &ar + 1 produce the same result?
In case of arrays like int array[2];
The array
pointer value is equal to &array
pointer value. It is a quirk of C, that applying the address-of operator to an array results in array pointer to the same memory. Through array
has a type of int[2][2]
, but &array
has the type of int(*)[2][2]
, ie. it is a pointer to 2d array.
Because the type changes, the pointer arithmetics changes. Teh typeof(ar)
decays to typeof(int(*)[2])
so the ar + 1
is equal to
`(uintptr_t)ar + sizeof(int[2]) * 1`.
But because the typeof(&ar) == typeof(int(*)[2][2])
the &ar + 1
is equal to
`(uintrpt_t)ar + sizeof(int[2][2]) * 1`.
hence the difference in pointer value when incrementing the pointer, as sizeof(int[2][2])
is equal to sizeof(int) * 2 * 2
.
I think you fail to grasp that in case of 2d arrays, the "first" level is 1d array of two elements, than the second is an int. So typeof(ar[0])
is an array of two int elements.
Your code has UB, as the %p
modifer should be used only with void*
pointers. It's best to remember (or at least know that you should) to printf("%p", (void*)&ar[1][0] + 1);
cast your pointers.
Upvotes: 0
Reputation: 134386
In your case, ar
is an array. Hence, first of all, remember
ar
is type of int [2][2]
, which is an array of array of int
s&ar
is of type int (*)[2][2]
, i.e., pointer to an array of array of 2 int
s.That said, array type, in cases, decay to the pointer to the first element of the array.Note
So, in case of an expression like
ar + 1
is just the same as
(&(ar[0])) + 1;
which basically points to ar[1]
.
What I don't understand is why the difference between
ar + 1
andar
is only 8 bytes
So, the "difference" here, is by the size occupied by the elements of ar[0]
, which is , 2 ints
, which is, in your platform, 8 bytes. Result checks out.
On the other hand, for an expression like
&ar + 1;
it operates on the pointer type (as mentioned earlier), and points to the location one past the last element in the array. So, the difference is, for 2 arrays of 2 int
s each, hence (2*2*4) = 16 bytes.
Note:
Quoting C11
, chapter §6.3.2.1
Except when it is the operand of the
sizeof
operator, the_Alignof
operator, or the unary&
operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. [....]
Upvotes: 1
Reputation: 214780
ar
, when used in an expression, "decays" to a pointer to the first element. In this case, arr + 1
gives arithmetic on a pointer of type int (*)[2]
. Which points to an int [2]
with size 8 bytes.
This rule of "array decay" is specified in C17 6.3.2.1 §3:
Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue
So when you type &ar
, you get the special exception from the array decay rule, no decay takes place but you actually get an int (*)[2][2]
as expected. And therefore &ar + 1
gives 16 bytes.
Upvotes: 1