Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning multidimensional arrays from a function in C

What is the best way to return a multidimensional array from a function in c ?

Say we need to generate a multidimensional array in a function and call it in main, is it best to wrap it in a struct or just return a pointer to memory on the heap ?

 int *create_array(int rows, int columns){
     int array[rows][columns] = {0};
     return array;
 }

 int main(){

     int row = 10;
     int columns = 2;
     create_array(row,columns); 
 }

The code above, is just to sketch out the basic program I have in mind.

like image 654
NiallJG Avatar asked Jul 30 '17 07:07

NiallJG


2 Answers

This is wrong:

int *create_array(int rows, int columns){
     int array[rows][columns] = {0};
     return array;
}

and should produce a warning like this:

prog.c:2:6: note: (near initialization for 'array')
prog.c:3:13: warning: return from incompatible pointer type [-Wincompatible-pointer-types]
      return array;
             ^~~~~
prog.c:3:13: warning: function returns address of local variable [-Wreturn-local-addr]

since you are returning the address of an automatic variable; its lifetime ends when its corresponding function terminates.


You should either declare a double pointer in main(), pass it through the function, dynamically allocate memory for it and return that pointer. Or you could create the array in main() and pass the double pointer to the function.


I want to know ways to allocate multidimensional arrays on the heap and pass them around

For allocating memory on the heap you could use one of these two methods, which involve pointers:

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

// We return the pointer
int **get(int N, int M) /* Allocate the array */
{
    /* Check if allocation succeeded. (check for NULL pointer) */
    int i, **array;
    array = malloc(N*sizeof(int *));
    for(i = 0 ; i < N ; i++)
        array[i] = malloc( M*sizeof(int) );
    return array;
}

// We don't return the pointer
void getNoReturn(int*** array, int N, int M) {
    /* Check if allocation succeeded. (check for NULL pointer) */
    int i;
    *array = malloc(N*sizeof(int *));
    for(i = 0 ; i < N ; i++)
        (*array)[i] = malloc( M*sizeof(int) );
}

void fill(int** p, int N, int M) {
    int i, j;
    for(i = 0 ; i < N ; i++)
        for(j = 0 ; j < M ; j++)
            p[i][j] = j;
}

void print(int** p, int N, int M) {
    int i, j;
    for(i = 0 ; i < N ; i++)
        for(j = 0 ; j < M ; j++)
            printf("array[%d][%d] = %d\n", i, j, p[i][j]);
}

void freeArray(int** p, int N) {
    int i;
    for(i = 0 ; i < N ; i++)
        free(p[i]);
    free(p);
}

int main(void)
{
    int **p;
    //getNoReturn(&p, 2, 5);
    p = get(2, 5);
    fill(p ,2, 5);
    print(p, 2, 5);
    freeArray(p ,2);
    return 0;
}

Pick whichever suits best your style.

like image 176
gsamaras Avatar answered Sep 26 '22 19:09

gsamaras


What is the best way to return a multidimensional array from a function in c ?

My recommendation is to avoid doing that, and avoid multidimensional arrays in C (they are unreadable and troublesome).

I would recommend making your matrix type your proper abstract data type, represented by some struct ending with a flexible array member:

struct mymatrix_st {
  unsigned nbrows, nbcolumns;
  int values[];
};

Here is the creation function (returning a properly initialized pointer to dynamic memory):

struct mymatrix_st*
create_matrix(unsigned mnbrows, unsigned mnbcolumns) {
  if (mnbrows > UINT_MAX/4 || mnbcolumns > UINT_MAX/4
      ||(unsigned long)mnbrows * (unsigned long)mnbcolums
        > UINT_MAX) {
   fprintf(stderr, "too big matrix\n");
   exit(EXIT_FAILURE);
 };
 size_t sz = sizeof(struct mymatrix_st)+(mnbrows*mnbcolumns*sizeof(int));
 struct mymatrix_st*m = malloc(sz);
 if (!m) { 
   perror("malloc mymatrix"); exit(EXIT_FAILURE); };
 m->nbrows = mnbrows;
 m->nbcolumns = mnbcolumns;
 for (unsigned long ix=(unsigned long)mnbrows * (unsigned long)mnbcolumns-1;
      ix>=0; ix--)
   m->values[ix] = 0;
 return m;;
} /*end create_matrix*/

It is on purpose that struct mymatrix_st don't contain any interior pointer. You can and should use free to destroy it.

Here is the accessor function; make it a static inline function and define it in the same header declaring struct mymatrix_st and create_matrix, e.g.

static inline int getmatrix(struct mymatrix_st*m, unsigned row, unsigned col) {
  if (!m) {
     fprintf(stderr, "getmatrix with no matrix\n");
     exit(EXIT_FAILURE);
  };
  if (row >= m->nbrows || col >= m->nbcolumns){
     fprintf(stderr, "getmatrix out of bounds\n");
     exit(EXIT_FAILURE);
  };
  return m->values[row*m->nbcolumns + col];
}

I leave up to you to define and implement the other operations on your abstract struct mymatrix_st type.

(you could adapt the code, perhaps removing the out of bound check, but I don't recommend unsafe code)

like image 24
Basile Starynkevitch Avatar answered Sep 26 '22 19:09

Basile Starynkevitch