arrayscmultidimensional-arraydynamic-memory-allocation

How memory address for pointer to arrays is same as an element in 2D array?


Consider the following code:

#include <stdio.h>

int main()
{
    int **s = (int**)malloc(3 * sizeof(int*));
    for (int i = 0; i < 3; i++)
    {
        printf("%d\n", s+i);
    }

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

    for (int i = 0; i < 3; i++)
    {
        printf("%d\n", s+i);
    }
    printf("\n");
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            printf("%d\n", *(s+i)+j);
        }
    }
    printf("\n");
    int s2[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    for (int i = 0; i < 3; i++)
    {
        printf("%d\n", s2+i);
    }
    printf("\n");
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            printf("%d\n", *(s2+i)+j);
        }
    }
}

The output in terminal was:

Output

You can see when I dynamically allocated memory, the memory address where the pointer to each row was stored is different from the address of the first element of each of those rows, which seems expected to me. But in case of defining a 2D array, the address where the pointer to each row array is stored is same as the address of the first element of each of the row. It seems to me that in the same memory location, a pointer to array and an element is kept, which seems unexpected to me. Can anyone explain it in detail?


Solution

  • The correct conversion format specification for printing a pointer is %p, not %d. By using the wrong conversion format specification, your program is invoking undefined behavior. Using %d may work on some platforms, but may fail on other platforms.

    Also, in C, there is no reason to cast the result of malloc. See the following question for further information:

    Should I cast the result of malloc (in C)?


    In your question, you wrote:

    But in case of defining a 2D array, the address where the pointer to each row array is stored is same as the address of the first element of each of the row.

    When you define a 2D array, there is no such thing as a "pointer to each row".

    The difference between a 2D array and an array of pointers to rows is that the latter has a layer of indirection (i.e. a layer of pointers which must be dereferenced), while the former does not. A 2D array is simply an array of arrays, which is stored contiguously.

    Unless you have a jagged array, there is no reason for this level of indirection and you can use a 2D array instead.

    When using an array in an expression, in most situations, this array will decay to a pointer to the first element of the array. This is what is happening in this line of your code:

    printf("%d\n", s2+i);
    

    In that line, writing s2 is equivalent to writing &s2[0].

    The same applies to this line:

    printf("%d\n", *(s2+i)+j);
    

    You appear to be under the impression that when dynamic memory allocation is used, you can't create a 2D array and must create an array of pointers to rows instead. However, this impression is incorrect. You can dynamically allocate memory for a 2D array of size 3x3 like this:

    int (*s)[3] = malloc(sizeof(int[3][3]));
    

    The declaration int (*s)[3] may be hard to understand at first. But if we read it inside out, it is easy:

    This means that the declared pointer s is of type "pointer to array of 3 elements of type int". So it is a pointer to the first row of the 3x3 2D array. This means that you can use this pointer to index into the 2D array, for example like this:

    s[2][2]
    

    This works because s[2] will yield a (one-dimensional) sub-array which represents a single row. You can index into this sub-array like any other one-dimensional array.

    Note that the parentheses in (*s) are necessary, because int *s[3] would declare an array of 3 elements of type int *, whereas int (*s)[3] declares a pointer to an array of 3 elements of type int. This is because the [] subscript operator has higher precedence than the * dereference operator.

    You may find the web site cdecl.org useful in decoding complex C declarations.