简体   繁体   中英

Understanding allocated memory addresses in array elements in C (gcc in Windows 10)

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?

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] , ie, 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.

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 and ar 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. [....]

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM