3

So I read dozens of examples of passing an 2D array pointer to function to get/change values of that array in function. But is it possible to create (allocate memory) inside the function. Something like this:

#include <stdio.h>

void createArr(int** arrPtr, int x, int y);

int main() {

    int x, y;       //Dimension
    int i, j;       //Loop indexes
    int** arr;      //2D array pointer
    arr = NULL;
    x=3;
    y=4;

    createArr(arr, x, y);

    for (i = 0; i < x; ++i) {
        for (j = 0; j < y; ++j) {
            printf("%d\n", arr[i][j]);
        }
        printf("\n");
    }
    _getch();    
}

void createArr(int** arrPtr, int x, int y) {
    int i, j;       //Loop indexes
    arrPtr = malloc(x*sizeof(int*));
    for (i = 0; i < x; ++i)
        arrPtr[i] = malloc(y*sizeof(int));

    for (i = 0; i < x; ++i) {
        for (j = 0; j < y; ++j) {
            arrPtr[i][j] = i + j;
        }
    }    
}
Marcin
  • 35
  • 4

3 Answers3

3

Forget about pointer-to-pointers. They have nothing to do with 2D arrays.

How to do it correctly: How do I correctly set up, access, and free a multidimensional array in C?.

One of many reasons why it is wrong to use pointer-to-pointer: Why do I need to use type** to point to type*?.

Example of how you could do it properly:

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


void* create_2D_array (size_t x, size_t y)
{
  int (*array)[y] = malloc( sizeof(int[x][y]) );

  for (size_t i = 0; i < x; ++i) 
  {
    for (size_t j = 0; j < y; ++j) 
    {
      array[i][j] = (int)(i + j);
    }
  }

  return array;
}

void print_2D_array (size_t x, size_t y, int array[x][y])
{
  for (size_t i = 0; i < x; ++i) 
  {
    for (size_t j = 0; j < y; ++j) 
    {
      printf("%d ", array[i][j]);
    }
    printf("\n");
  }
}


int main (void)
{
  size_t x = 5;
  size_t y = 3;

  int (*arr_2D)[y];

  arr_2D = create_2D_array(x, y);

  print_2D_array(x, y, arr_2D);

  free(arr_2D);

  return 0;
}
Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 1
    When I try to use your version it say that : size_t y expression must have constant value. So you can't make arrays like this dynamically (as I said...) – Marcin Apr 20 '16 at 14:06
  • 1
    @Marcin I did compile and test this code. You must use a compiler made after the year 1999 (why wouldn't you?). For example GCC. You can tell GCC to be a C compiler with the compiler options `gcc -std=c11 -pedantic-errors`. – Lundin Apr 20 '16 at 14:10
  • I spent whole day today and can't figure out one think. Everything from your example seams to work but I can't figure out this. If I print table by function `print2DArray(xDim, yDim, arr);` I get all elements so it works fine. If I print one element like this `printf( "%d\n", arr[0][0])` it is fine to. BUT if I print like this `printf( "%d\n",old[1][1]);` or any other value different than 0, I get segmentation fault. Please help me. – Marcin Apr 21 '16 at 19:08
  • @Marcin What is "old"? – Lundin Apr 21 '16 at 19:49
  • Sorry. "old" is the name of array that I use in my code, but here i pasted it as `arr`. So it should be `printf( "%d\n",arr[1][1])`. The names are fine in code. – Marcin Apr 21 '16 at 20:15
  • @Marcin Well, then something in the code is wrong :) Please post a separate question. – Lundin Apr 21 '16 at 20:55
  • Here is the link to question: http://stackoverflow.com/questions/36782015/2d-array-pointer-access-segmentation-fault-in-c – Marcin Apr 21 '16 at 23:15
2

Yes, passing a pointer to int ** (but 3 stars is considered bad style), I suggest to return an allocated variable from your function:

int **createArr(int x, int y)
{
    int **arrPtr;
    int i, j;       //Loop indexes

    arrPtr = malloc(x*sizeof(int*));
    if (arrPtr == NULL) { /* always check the return of malloc */
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    for (i = 0; i < x; ++i) {
        arrPtr[i] = malloc(y*sizeof(int));
        if (arrPtr[i] == NULL) {
            perror("malloc");
            exit(EXIT_FAILURE);
        }
    }
    for (i = 0; i < x; ++i) {
        for (j = 0; j < y; ++j) {
            arrPtr[i][j] = i + j;
        }
    }
    return arrPtr;   
}

Call it using:

arr = createArr(x, y);
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • 2
    You should check all return values of `malloc`, not only the first one. – mch Apr 20 '16 at 13:16
  • 1
    There is no reason to create a segmented pointer-to-pointer thing. Instead, when you want a 2D array, use a 2D array. – Lundin Apr 20 '16 at 13:16
  • Soo.. I can do it like this: void createArr(int*** arrPtr, int x, int y); And here arrPtr is a pointer which point to a **int, which can contain addresses for arrays. But it looks bad. And another way ,more elegant, is the one you showed. Thank you very much for quick answers! – Marcin Apr 20 '16 at 13:17
  • 1
    @Marcin It looks bad because it is bad. You shouldn't use any pointer-to-pointer to begin with. You should use real 2D arrays, assuming 2D arrays is what you want. – Lundin Apr 20 '16 at 13:18
  • @Lundin But I have to create this array dynamically (don't know the dimension before runtime), so I have to do it like this. – Marcin Apr 20 '16 at 13:20
  • Consider for example: `void crash_and_burn (void) { int** wannabe_array = createArr(5, 3); int** wannabe2 = createArr(5, 3); memcpy(wannabe2, wannabe_array, sizeof(int[5][3])); }` – Lundin Apr 20 '16 at 13:21
  • @Marcin No, see my example. VLAs (and more importantly pointers to VLAs) have been in C since the year 1999. – Lundin Apr 20 '16 at 13:22
  • This may appear to work as a 2D array when using `[i][j]` access method but it is not the same as `int arr[x][y]` – Support Ukraine Apr 20 '16 at 13:25
  • @4386427 No it may never work like a 2D array. Arrays are allocated in adjacent, contiguous memory. Just because you use the `[ ]` operator on a pointer, the pointer doesn't magically turn into an array. Run my "crash_and_burn" example above, for example. Disclaimer: it might cause your computer to crash and burn, since pointers are not arrays. – Lundin Apr 20 '16 at 13:26
  • @Lundin - That was actually what I tried to say in my comment. Since everything is ok as long accesses are done using `[i][j]`one migth considered it to be the same as `int arr[x][y]` but it isn't. – Support Ukraine Apr 20 '16 at 13:35
  • @Lundin To sum up all the information: 1) I don't really get why my version (void createArr(int** arrPtr, int x, int y)) didn't work. Assuming that want to allocate array like this, because I'm stupid why it doesn't work. I pass the pointer, so why it's work in main function, but not in my function. If I will understand this, I promise I will allocate 2D arrays like this: int (*array)[y] = malloc( sizeof(int[x][y]) ); 2) Your crash_and_burn() example won't work because we start to copy the memory from arr* (so not the data, but some random memory space)? – Marcin Apr 20 '16 at 13:57
  • @Marcin It won't work for the same reason as why `void func (int x) { x=5; }` won't change any variables in the caller. You didn't attempt a plain variable though, you attempted to allocate a pointer to an array of pointers. Yet your pointer to an array of pointers was only changed locally inside the function, like `x` in my example. – Lundin Apr 20 '16 at 14:02
  • @Marcin The crash and burn example will crash and burn because it used a pointer to an array of pointers instead of a real 2D array. – Lundin Apr 20 '16 at 14:03
0

Yes, an array can be initialized this way. As long as you pass a pointer, the memory address should remain the same. So, if you assign anything to the pointer it will valid.

Think of an a[] as a* pointer to the first element

a [][] will be a** pointer to a pointer of the first element or a pointer to the first array (first row of a table)

Striker
  • 507
  • 4
  • 11