0

I've just finished the С language course at the university. I was told how variables are stored in the pc memory and that arays are stored successively. That's seen in following example:

#include <stdio.h>
void main(){
 char a[3][6];
 printf("[0][0]->%p \t[1][0]->%p\t [0][5] %p \n", &a[0][0],&a[1][0],  &a[0][5]);
 printf("%p %p", a, &a[0][0]);
}

The output will be like:

[0][0]->0061FF06        [1][0]->0061FF0C         [0][5] 0061FF0B
0061FF06 0061FF06

AND! The adress of a will be the same as the adress of a[0][0]. And the sizeof(a) is 18(in this example). But... when it comes to dynamic arrays... Here, see for yourself.

#include <stdio.h>
#include <stdlib.h>
void main()
{
    char **a = (char **)malloc(3 * sizeof(char*));
    for(int i=0; i < 3; i++)
        a[i] = (char *)malloc(6 * sizeof(char));

    printf("[0][0]:%p [1][0]%p [0][5]%p\n", &a[0][0], &a[1][0], &a[0][5]);
    printf("a:%p [0][0]: %p\n", a, &a[0][0]);
    printf("[1][0] - [0][5] = %d \n", &a[1][0] - &a[0][5]);
    printf("%d", sizeof(a) );
}

The resukt will be like:

[0][0]:00AD1588 [1][0]00AD1598 [0][5]00AD158D
a: 00AD15D8 [0][0]: 00AD1588
[1][0] - [0][5] = 11
4
  1. WHY in dynamic [1][0] - [0][5] isn't equal to 1 (as in static arrays) ?
  2. WHY in dynamic sizeof(a) is 4 ?
  3. WHY in dynamic adresses of a and &a[0][0] don't concide ?
Jack
  • 131,802
  • 30
  • 241
  • 343
Prog_man
  • 5
  • 2
  • 1
    Because your `dynamic array` as you call it is actually a pointer, which behaves very differently. – tkausl Feb 05 '21 at 21:12
  • "arays are stored successively" - this isn't true - I think you mean array **elements** are stored sequentially. – Dai Feb 05 '21 at 21:12
  • Why, because a 2D dynamic array is different from a 2D static array, (the former is not stored continuously to give one simple difference) **WHY** do you think they should be the same? – john Feb 05 '21 at 21:12
  • 1
    @john Time to give the class prof a low score on the class evaluation forms, methinks. – Dai Feb 05 '21 at 21:14
  • @john I want to understand where did the adress printf("%p", a); come from – Prog_man Feb 05 '21 at 21:16
  • @Prog_man Are you asking about the static array or the dynamic array? – john Feb 05 '21 at 21:17
  • 2
    Your two examples are _not_ equivalent. When you have `a[3][6]`, all elements are contiguous in memory. When you do: `int **a = malloc(...);` and then loop to fill in with `malloc`, the array of _pointers_ in `a` are contiguous, but each element of `a` is allocated separately, and will not be contiguous. If you want a dynamic array that is contiguous, do: `int (*a)[3][6] = malloc(sizeof(int) * 3 * 6);` – Craig Estey Feb 05 '21 at 21:18
  • @john about dynamic – Prog_man Feb 05 '21 at 21:19
  • The OP should read this to help dispel their misunderstanding: https://stackoverflow.com/questions/2565039/how-are-multi-dimensional-arrays-formatted-in-memory – Dai Feb 05 '21 at 21:22
  • @Prog_man it's the value of the pointer returned by the first call to `malloc` in your code. I'm guessing your real confusion is that you don't understand how `malloc` works. – john Feb 05 '21 at 21:23
  • @john I know how `malloc` works. But for example in static `a` means `a[0][0]` (the adresses are the same), but in dynamic thus rule doesn't work – Prog_man Feb 05 '21 at 21:30
  • @Prog_man That's because you haven't allocated a contiguous block of memory. In the dynamic array `a[0]` is a different block of memory from `a`. `a` is the pointer returned by the first call to malloc but `a[0]` is the pointer returned by the second call to malloc. `a[i] = (char *)malloc(6 * sizeof(char));` (with `i` equal to zero). – john Feb 05 '21 at 21:37

3 Answers3

4

In your first example, a is a 1-dimensional array with 3 elements, where each element is a char[6] array. Since the elements of a given array are sequential in memory, the entire array is sequential:

-------------------------------------------------------------------------------------
| ------------------------- | ------------------------- | ------------------------- |
| | 0 | 1 | 2 | 3 | 4 | 5 | | | 0 | 1 | 2 | 3 | 4 | 5 | | | 0 | 1 | 2 | 3 | 4 | 5 | |
| ------------------------- | ------------------------- | ------------------------- |
-------------------------------------------------------------------------------------
             a[0]                        a[1]                        a[2]

However, in your second example, a is a pointer to a 1-dimensional array with 3 elements, where each element is a pointer to a separate char[6] array located elsewhere in memory, wherever malloc() decided to allocate them:

                                 -------------------------
                                 | 0 | 1 | 2 | 3 | 4 | 5 |
-------------------------        -------------------------
| 0 | 1 | 2 | 3 | 4 | 5 |        ^
-------------------------        a[1]
^                                |
a[0]              ----------------
|                 |
|                 |     -------------------------
|                 |     | 0 | 1 | 2 | 3 | 4 | 5 |
|                 |     -------------------------
|                 |     ^
|__________       |     a[2]
           |      |     |
         -------------------
         |  0  |  1  |  2  |
         -------------------
         ^
         a
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

You declared a pointer with the name a

char **a = (char **)malloc(3 * sizeof(char*));

In your system sizeof( char ** ) that is the same as sizeof( a ) is equal to 4.

That is the size of the pointer has nothing common with the size of the dynamically allocated array. It only stores the address of the allocated extent for the array.

As for other questions then you did not allocate a two-dimensional array. You allocated separately four arrays. One array has elements of the type char * and each element of the array points to a separately allocated array with elements of the type char.

So for example the answer to this your question

WHY in dynamic adresses of a and &a[0][0] don't concide

is the following.

The variable a has its one address. It has nothing common with the allocated dynamically one-dimensional array with the element type char * except that the variable a stores the address of the allocated array.

By the way, this has nothing common with the dynamic memory allocation.

Consider the following demonstrative program.

#include <stdio.h>

int main(void) 
{
    char a[3][6];
    char ( *p )[6] = a;
    
    printf( "a = %p\n", ( void * )a );
    printf( "p = %p\n", ( void * )p );
    printf( "&p = %p\n", ( void * )&p );

    return 0;
}

Its output might look like

a = 0x7ffc08157b50
p = 0x7ffc08157b50
&p = 0x7ffc08157b48

As you can see the address of the variable p (&p) differs from the address of the extent occupied by the array because the variable p is a separate variable that occupies its own extent of memory.

As for arrays then array designators used in expressions are implicitly converted to pointers to their first elements.

You could get the expected result (except the result relative to the sizeof operator) if you indeed will allocate a two-dimensiona array like

char ( *a )[6] = malloc( sizeof( char[3][6] ) );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

It helps if you understand how arrays vs. pointers-to-arrays are represented in memory.

// This can be on the heap or on the stack, the representation is the same:
char a[3][6];

If a is at 0x1000 then it looks like this:

  • Each sub-array of char[6] is grouped in square-brackets: [00 00 00 00 00 00], and all sub-arrays have the same value, as indicated:
    • a[0][0..n] = 0x00
    • a[1][0..n] = 0x11
    • a[2][0..n] = 0x22
           0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
------------------------------------------------------------------------
0x1000   [00  00  00  00  00  00][11  11  11  11  11  11][22  22  22  22     
0x1010    22  22] 00  00  00  00  00  00  00  00  00  00  00  00  00  00
0x1020    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00

You can see the 18 contiguous elements from 0x1000 to 0x1011.


Now this:

char **a = (char **)malloc(3 * sizeof(char*));
for(int i=0; i < 3; i++) {
    a[i] = (char *)malloc(6 * sizeof(char));
}

...looks like this:

  • Assuming 32-bit big-endian pointers (big-endian for clarity, in reality x86 is little-endian).
  • a exists at 0x1000.
    • a[0] points to 0x1030
    • a[1] points to 0x1040
    • a[2] points to 0x1050
  • Each sub-array elements all have the same value, as above, and is indicated by square-brackets:
    • a[0][0..n] = 0x00
    • a[1][0..n] = 0x11
    • a[2][0..n] = 0x22
           0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
------------------------------------------------------------------------
0x1000   [00  00  10  30][00  00  10  40][00  00  10  50] 00  00  00  00 
0x1010    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
0x1020    00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
0x1030   [00  00  00  00  00  00] 00  00  00  00  00  00  00  00  00  00
0x1040   [11  11  11  11  11  11] 00  00  00  00  00  00  00  00  00  00
0x1050   [22  22  22  22  22  22] 00  00  00  00  00  00  00  00  00  00

Now you see the allocated size of a (itself) is 12 bytes, and the entire 2D array structure is no-longer contiguous in memory - which is why your pointer subtraction result is meaningless.

Dai
  • 141,631
  • 28
  • 261
  • 374