0

totally a newbie in C. I'm trying to put a 2D array into an existing memory space created by malloc. Here is the code:

int main()
{
    int **a;  //double pointer
    int i;
    void *ptr = malloc(sizeof(int)*4);  //2x2 array
    a = (int **)ptr;  //start of the array
    for(i=0; i<2; i++)  
            a[i] = (int *)a + i*2;
    printf("a: %p\n", a);
    printf("a[0]: %p\n", a[0]);
    printf("a[1]: %p\n", a[1]);
}

output:

a: 0x1976010
a[0]: 0x1976010
a[1]: 0x1976018

but when I try to access the element:

for(i=0; i<2; i++)
    for(j=0; j<2; j++)
        a[i][j] = j + i*10;

I got segment fault. Where did I do wrong? Thanks in advance!

27Blend
  • 105
  • 1
  • 8
  • 1
    `int **a` is a pointer to pointer. It's not a 2D array and cannot represent one. If you want a 2D array, use one. Sidenote: "double pointer" is often confused as `double *`. And never ever use casts if you don't really understand just to silence the compiler. – too honest for this site Sep 19 '18 at 12:05

5 Answers5

3

If you have an existing (aka already allocated) memory area that you want to use for a 2D array (or rather for an array of array), you should not use a double pointer. Instead you should use a pointer to array of size N

Like

int (*a)[2];  // declare a as a pointer to array (size 2) of int

A complete program could be:

int main()
{
    // Allocate a memory area for holding 4 int
    void *ptr = malloc(sizeof(int)*4);

    int (*a)[2];  // a as a pointer to array (size 2) of int
    a = ptr;      // make a point to your memory area

    // Now a can be used as a "2D array" and data will be stored in the malloc'ed memory
    for (int i=0; i<2; ++i)
        for (int j=0; j<2; ++j)
            a[i][j] = i*100 + j;

    for (int i=0; i<2; ++i)
        for (int j=0; j<2; ++j)
            printf("a[%d][%d]=%d\n", i, j, a[i][j]);

    free(ptr);
    return 0;
}

Output:

a[0][0]=0
a[0][1]=1
a[1][0]=100
a[1][1]=101
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
2

When it comes to dynamic 2D arrays, there's unfortunately a lot of myths and hogwash, widely used bad practice and so on. Forget about pointer-to-pointers, is needlessly complex, error prone and slow.

A detailed explanation of the correct way to do this can be found here: Correctly allocating multi-dimensional arrays.

The correct way to allocate a 2D array dynamically is this:

#include <stdlib.h>
#include <stdio.h>

int main()
{
    const size_t X = 2;
    const size_t Y = 2;

    int (*a)[Y] = malloc( sizeof(int[X][Y]) );
    int count = 1;

    for(size_t x=0; x<X; x++)
    {
      for(size_t y=0; y<Y; y++)
      {
        a[x][y] = count++; // assign some value here
        printf("%d ", a[x][y]);
      }
      printf("\n");
    }

    free(a);
}

Where int (*a)[Y] is an array pointer to the first element of the 2D array. The first element being a 1D array of type int [Y].

danglingpointer
  • 4,708
  • 3
  • 24
  • 42
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • `sizeof(*a) * X` would be better, as it does not rely on the type of the elements and avoids the redundant usage of the inner dimension. – too honest for this site Sep 19 '18 at 12:09
  • @toohonestforthissite Not really, since the very same line already relies on the type in the array declaration. An alternative syntax could be used, however: `int (*a2)[X][Y] = malloc( sizeof *a2); int (*a)[Y] = a2[0];` After which free() could be called on either `a2` or `a`. – Lundin Sep 19 '18 at 12:29
  • I talked about redundancy. With my version you can change the basse type just in one place. Such redundancy is a common error. It becomes more severe if the allocation is not in an initialiser, but an assignment e.g. combined with a null pointer test. Your alternative is uncommon, as it uses an unnecessary intermediat stage (or, if you omit the second pointer) clumsy code at leat it pullutes the namespace even more). Re. `free(a)` for the latter is formally correct, but irritating on reviews/maintenance, as the type differs from the intended during allocation. – too honest for this site Sep 19 '18 at 12:35
  • @toohonestforthissite Using your argumentation, we should then stop writing `int x = (int)y;` and instead write `{typedef int typex; typex x = (typex)y;` ... or maybe write all code inside "X macros". That's of course just silly - the bottom line when programming is _you must actually know what you are doing_. If you don't, no tricks against redundant code will help you. My experience from a whole lot of SO questions is that introducing tricks against writing redundant code far more often causes more harm/bugs than the danger they were supposed to protect against. – Lundin Sep 19 '18 at 12:41
  • It's obviously not the same, as `typex` is used here dedondantly. If anything, it would be `int x = (typeof(x))y;`, which uses a gcc extension. While not a bad idea, it comes at some typing overhead and often scalar types are not really changed as arrays might (yeah, that's weak argument, but it adds up). Can you tell me what's the problem with the commonly accepted use I propose? And what's the advantage of yours? After all that's what counts. The site is to advocate code quality, not "beginner friendly" sloppy code. And that's not a "trick", but actually helps learning the language semantics – too honest for this site Sep 19 '18 at 13:17
1

You got segmentation fault because a (a double pointer) is actually having a single pointer value.

There are multiple ways to use 2D array. The following is one of them.

#define ROW (2)
#define COL (3)

int main()
{
    int (*a)[COL] = malloc(sizeof(int[ROW][COL]));
    if(NULL == a) return -1; //Check if allocation is successfull.
    int i, j;
    for(i=0; i<ROW; i++)  
       for(j=0; j< COL; j++)
            a[i][j] = i*ROW + j; //Assign some values.
    printf("a: %p\n", a);
    printf("a[0][0]: %d\n", a[0][0]); //Printing a value.

    free(a); //Free the allocated memory.
}
MayurK
  • 1,925
  • 14
  • 27
  • 2
    You have some bugs here. The form `int (*a)[ROW][COL] ` is a bit complicated, as you have to de-reference the array pointer to get to the actual 2D array. Meaning that `a[i][j] = ...` is wrong here, it needs to be `(*a)[i][j] = ...`. It's easier to use an array pointer to the first element instead, to get rid of this complicated syntax. – Lundin Sep 19 '18 at 11:10
  • Oh Ok. So to use a[i][j] =.. i should use (*a)[COL] = malloc right? Saw your answer. Thanks! – MayurK Sep 19 '18 at 11:13
  • Yeah. Formally, using a pointer to a 2D array is the most correct form. If using it, we could for example write `int (*a)[ROW][COL] = malloc(sizeof *a)` which isn't otherwise possible. But since the syntax gets burdensome, it is often better to skip one dimension. – Lundin Sep 19 '18 at 12:25
0

EDIT

This isn't a correct answer, as pointed out by Lundin. There are much better ways to do this I wasn't aware of!

This line

a[i][j] = j + i*10;

is treating a[0] and a[1] as pointers themselves to other arrays, so it's reading their values

a[0]: 0x1976010
a[1]: 0x1976018

as memory locations, and segfaulting.

If you want to dynamically allocate 2d arrays, allocate one dimension of int*s, then iterate through it and allocate memory to each of the indices as ints.

int main()
{
    #define DIM_X 2
    #define DIM_Y DIM_X

    int i = 0;
    int j = 0;
    int** x = 0;
    void* a = malloc(sizeof(int*)*DIM_X);

    x = (int**)a;
    for (i = 0; i < 2; i++)
    {
        x[i] = (int*)malloc(sizeof(int)*DIM_Y);
    }

    // ...

    for (i = 0; i < DIM_X; i++)
    {
        free(x[i]);
    }
    free(x);

    return 0;
}

You could also do some tricky logic to keep everything contiguous, but it may be harder to read.

for (i = 0, j = 0; i < 2; i++)
{
    a[(i * 2) + j] = j + (i * 10);

    j++;
    if (2 < j) j = 0;
}
Sean Monroe
  • 173
  • 5
  • 2
    The pointer-to-pointer is not a 2D array, nor can it point to one. This is something else and it is fragmented all over the heap, which gives bad performance, for nothing gained but extra complexity. Furthermore, "mangled" 2D arrays like `a[(i * 2) + j]` is 1990s programming. In the current millennia we have pointer to VLA so we don't need to use that trick any longer. – Lundin Sep 19 '18 at 11:14
  • @Lundin Thank you for the explanation. I'm unfortunately one of the group who was taught a bad habit. I'll edit to discourage using my posted solution. – Sean Monroe Sep 19 '18 at 11:21
0

You have to allocate also storage for the pointers.

int main()
{
    int **a;  //double pointer
    int i;
    void *ptr = malloc(sizeof(int *)*2+sizeof(int)*4);  //2x2 array
                            //plus 2xpointers
    a = (int **)ptr;  //start of the array
    for(i=0; i<2; i++)  
            a[i] = (int *)a + 2 + i * 2; //plus 2 because the first 2 elements
                    //are your pointers
    printf("a: %p\n", a);
    printf("a[0]: %p\n", a[0]);
    printf("a[1]: %p\n", a[1]);
}
AnArrayOfFunctions
  • 3,452
  • 2
  • 29
  • 66