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

dynamic memory allocation in c programming examples
dynamic memory allocation in c++ and c++
static memory allocation in c
contiguous memory allocation program in c
dynamic memory allocation for string in c
dynamic memory allocation in c pdf
memory fragmentation in c++
how to dynamically allocate a 1d array in c

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 ints, 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 ints á 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?

Dynamic Memory Allocation and Fragmentation in C and C++, In C and C++, it can be very convenient to allocate and de-allocate blocks of memory as The actual allocation of addresses to variables is performed by the it takes two parameters – the number of array elements and the size of each element The best way to understand memory fragmentation is to look at an example. Initial address of the array – address of the first element of the array is called base address of the array. Each element will occupy the memory space required to accommodate the values for its type, i.e.; depending on elements datatype, 1, 4 or 8 bytes of memory is allocated for each elements.

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.

C dynamic memory allocation, C dynamic memory allocation refers to performing manual memory management for dynamic However, the size of the array is fixed at compile time. This computes the number of bytes that ten integers occupy in memory, then requests that After allocation with malloc , elements of the array are uninitialized variables. Memory Addresses. Memory can be though of as an array of bytes where each address is on index in the array and holds 1 byte. If a computer has 4K of memory, it would have 4096 addresses in the memory array. How operating systems handle memory is much more complex than this, but the analogy provides an easy way to think about memory to get started.

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.

Compile time and runtime memory allocation, Memory allocation in C; Compile time memory allocation; Runtime And the address of reserved memory bytes should be referred/identified by number . Will occupy memory at compile time // static allocation static int c = 10; c++; scanf("%d", (parr + i)); } // Print array elements printf("Input elements: ")  Memory can be thought of simply as an array of bytes. In this array, every memory location has its own address -- the address of the first byte is 0, followed by 1, 2, 3, and so on. Memory addresses act just like the indexes of a normal array. The computer can access any address in memory at any time (hence the name "random access memory").

C++ Tutorial: Memory Allocation - 2020, When a function is called, memory is allocated for all of its locals. The function badPointer() returns the address of the local variable i. (More effective C++, Item #8 Understand the different meanings of new and delete, Scott Meyers) for 10 string object, then call the default string constructor for each array element. Calculate Memory Addresses in Arrays. Posted on September 5, 2019 by admin. ISC Year 2009: Each element of an array A[20][10] requires 2 bytes of storage. If the

Memory Layout of C Programs, A typical memory representation of C program consists of following sections. A data segment is a portion of virtual address space of a program, which Ex: static int i = 10 will be stored in data segment and global int i = 10 will also be stored The newly called function then allocates room on the stack for its automatic and  Dynamic Memory Allocation in C using malloc (), calloc (), free () and realloc () Since C is a structured language, it has some fixed rules for programming. One of it includes changing the size of an array. An array is collection of items stored at continuous memory locations. As it can be seen that the length (size) of the array above made is 9.

7. Memory : Stack vs Heap, You don't have to allocate memory by hand, or free it once you don't need it A key to understanding the stack is the notion that when a function exits, all of its On lines 10, 11 and 12 we declare variables: an int , a double , and an array of data type in C that store addresses in memory instead of storing actual values. Each type of variable in C consumes a certain number of bytes. For example, a standard int variable generally needs 4 bytes of memory. This can, however, vary by OS, processor, and so on. Anyway, let's say I declare a simple int variable: int monstersUnderBed; On a G4 running Tiger, this variable uses 4 bytes of memory.

Comments
  • You need to use () to set the order of precedence, [] has the highest precedence after (), your expressions will not be what you expect as they are currently written. eg. *ar[0] is not the same as (*ar)[0]
  • Good point, thanks. However, in my code, I want the address of ar[0], is it not sufficient with &ar[0] or should I use &(ar[0])?