1

I am trying to modify a 2D array from a void function.

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


void try_by_reference(int **arr){
    *arr = realloc(*arr, sizeof *arr * 2);
}

int main(int argc, char **argv){
   
    // declare dynamic 2d-array and allocate memory
    int (*arr)[2] = malloc(sizeof *arr * 10);
   
    // fill array
    for (int i=0; i<10; i++){
        arr[i][0] = i;
        arr[i][1] = i+10;
   
    }

    // declare and fill a simpler dynamic array
    int *tarr = malloc(sizeof(int) * 10);
    for (int i=0; i<10; i++)
        tarr[i] = i*2;

    try_by_reference(&tarr);
    try_by_reference(&arr);          <-- this gets warning

    free(arr);
    free(tarr);

    return 0;
}

Compiler says:

 warning: incompatible pointer types passing 'int (**)[2]' to parameter of type 'int **'

What am I doing wrong?

Thank you!

ryyker
  • 22,849
  • 3
  • 43
  • 87
aerijman
  • 2,522
  • 1
  • 22
  • 32
  • 3
    Given `int **arr`, what do you think `sizeof *arr` is? – Andrew Henle Dec 20 '20 at 23:54
  • Please note that this is not how [`realloc` should be used](https://stackoverflow.com/questions/44789295/correct-use-of-realloc). – Bob__ Dec 20 '20 at 23:57
  • @AndrewHenle I prefer not using "the type" but rather infer it from the variable for allocating memory. The type could change later? Please, let me know if I am wrong on this and how to improve. – aerijman Dec 21 '20 at 01:20
  • @Bob__, `*arr2 = realloc(arr, sizeof *arr * 2);` ? – aerijman Dec 21 '20 at 01:23
  • @aerijman You didn't answer my question. If `arr` is of type `int **`, **what type is `*arr`? And what is returned by `sizeof` when `sizeof` is passed that type?** – Andrew Henle Dec 21 '20 at 10:04
  • @AndrewHenle type of *arr is int*. sizeof returns 8 (bits, a byte). What am I missing? – aerijman Dec 21 '20 at 16:14

1 Answers1

2
_"I am trying to modify a 2D array from a void function."_  

Here are some tips, and fixes that will allow you to update memory to an array of two pointers to int. (see comment in-line with your code)

void try_by_reference(int **arr){
    //always use a temporary variable to call realloc, otherwise if failed attempt - memory leak will occur
    int *tmp = realloc(*arr, 2 * sizeof(*arr));//this effectively reduces memory from original 10, to 2 instances of int
    if(!tmp)//always check return of realloc, if it fails free original memory and return
    {
        free(*arr);
        //set pointer to NULL here to provide way to test before 
        //freeing later in process. (See 'Reference' below)
        *arr = NULL;//to prevent problems in subsequent free calls
        return;
    }
    else *arr = tmp;
}

int main(int argc, char **argv){
   
    // declare dynamic 2d-array and allocate memory
    int *arr[2] = {NULL, NULL};//this is an array of 2 pointers to int - each
                      //need to be allocated
                      //it will result in an array shaped as array[2][10]
                      //after following calls to malloc.
    arr[0] = malloc(10*sizeof(arr[0]));//original provides memory for 10 instances of int
    if(arr[0])
    {
        arr[1] = malloc(10*sizeof(arr[1]));
        if(arr[1])
        {
            // fill array
            //for (int i=0; i<10; i++){
            for (int i=0; i<10; i++){
                //arr[i][0] = i;
                //arr[i][1] = i+10;
                arr[0][i] = i;//switch indices 
                arr[1][i] = i+10;//switch indices
           
            }
        }
    }

    // declare and fill a simpler dynamic array
    int *tarr = malloc(sizeof(int) * 10);
    for (int i=0; i<10; i++)
        tarr[i] = i*2;

    try_by_reference(&tarr);
    
    //try_by_reference(&arr);          <-- this gets warning
    //pass address of each pointer to memory, one at a time
    try_by_reference(&(arr[0]));        
    try_by_reference(&(arr[1]));       

    //To prevent UB from calling free on an already freed pointer
    //test before calling free.  
    if(arr[0]) free(arr[0]);//need to free each of two pointers to memory
    if(arr[1] free(arr[1]);//...
    if(tarr) free(tarr);

    return 0;
}

Reference regarding why set pointer to NULL after freeing. If the call to realloc() fails, thus resulting in freeing the original pointer, setting the pointer == NULL provides a way to test before calling free() later in process, thus avoiding the potential of invoking undefined behavior (UB).

There are several ways to create varying shapes of nD arrays memory in C, some of them easier to update memory than the form int *arr[2]. But I stay with this form to illustrate specifically a way to update it. Although it requires more rigor to access elements, for a int[2][10] implemented by pointers, I prefer creating an int *arr = malloc(2*10*sizeof(*arr));. Observe the following examples for ease of use comparisons. (using a 2D like, but of different dimensions):

int arr1[3][6] = {{1,2,3,4,5,6},{7,8,9,10,11,12},{13,14,15,16,17,18}};
//same memory as 
int arr2[18] = {{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18}};

knowing that   *(arr1 + 2*6 + 5) == arr2[2][5] = 18;
               *(arr1 + 0*6 + 4) == arr2[0][4] = 5;
               *(arr1 + 1*6 + 0) == arr2[1][0] = 7;
//                      | |   |_2nd index range 0 - 5    
//                      | |_ constant -> sizeof(arr1[0]/arr1[0][0])
//                      |1st index range is from 0 - 2

The same is true for dynamic memory.  int **arr1 and *arr2

int **arr1 //requires 7 calls to malloc/free
int *arr2  //requires 1 call to malloc/free
ryyker
  • 22,849
  • 3
  • 43
  • 87