Home > database >  How to return a 2D array?
How to return a 2D array?

Time:06-07

I was searching for an efficient way to create a 2D array and return it, usually I make an array of pointers, allocate memory for each pointer, and return an **ptr. I was looking for another way to do it because then I have to free every single memory allocated.

I was reading online that you can allocate a 2D array like this: int (*arr)[n] = malloc( sizeof *arr * i ); In which ptr is a pointer, pointing to the adress of arr[n].

When I try to return arr, from the following function: int *array_of_smallest(int count);
I get: warning: assignment to ‘int *’ from incompatible pointer type ‘int (*)[n]’ [-Wincompatible-pointer-types]

If I initialise another pointer and point it to array, then return it, I'm only returning a 1D array.

I think I'm mixing diferent concepts and confusing myself.

With an array of pointers I don't have a problem, but I wanted a simplier way to create a 2D array, also easier to free, but I'm not being able to return that array from my function.

CodePudding user response:

You declared a pointer of the variable modified type type int ( * )[n].

int (*arr)[n] = malloc( sizeof *arr * i );

That is the variable n used in the declarator is not an integer constant expression.

In this case the function should have the return type void *. And it can be declared like

void * array_of_smallest(int count)
{
    int (*arr)[n] = malloc( sizeof *arr * i );
    //...
    return arr;
} 

In the caller you will write

int ( *arr )[n] = array_of_smallest( count );

In this declaration the value of n must be the same as used within the function.

If to use an integer constant expression like

int (*arr)[2] = malloc( sizeof *arr * 2 );

then the function declaration will look like

int ( * array_of_smallest(int count) )[2];

Or you can introduce a typedef name before the function declaration like

typedef int Array[2];

and in this case the function will look like

Array * array_of_smallest(int count);

CodePudding user response:

The fundamental issue with returning the array is that it is not possible to declare Variably-Modified Type (like a pointer to VLA) at the file scope.

The reason of the problem can be explained as follow. Your code behaves more or less like this:

typedef int T[n];

T* array_of_smallest(int count) { ... }

Unfortunately, defining such a type T is not possible because of two reasons:

  • n is usually not visible at file scope, except if it was a global variable
  • evaluation of n would require code execution at file scope which is forbidden

Workarounds

  1. Return void* as described in other answer.

  2. Pass a pointer to VLA in "by-reference" style, as a pointer to a pointer to array

void array_of_smallest(int count, int (**p)[n]) {
 ...
 *p = arr;
}
  1. Return a pointer to incomplete array type of int[]. It is not possible to return incomplete types but it is possible to return a pointer to incomplete types:
int (*array_of_smallest(int count))[] { ... }

or a bit cleaner with a help of typeof extension (feature in C23)

typeof(int[]) *array_of_smallest(int count) { ... }

And use it as in the void* case:

int ( *arr )[n] = array_of_smallest( count );

This solution provides type-checking for the element type, however it works only for 2-dimensional arrays.

  1. Wrap the pointer into a structure and use a macro to reconstruct its type on the client side:
typedef struct {
  int rows, cols;
  void* data;
} Arr2D;

Arr2D array_of_smallest(int count) {
  ...
  return (Arr2D) { .rows = i, .cols = n, .data = arr };
}

#define ARR2D_DEF_VIEW(view, arr2d) int (*view)[(arr2d).cols] = (arr2d).data

... usage ...

Arr2D arr = array_of_smallest(42);
ARR2D_DEF_VIEW(view, arr);

... do stuff with view[i][j]
}

Alternatively, use typeof extension to infer the type of VLA view rather than declare it in a macro:

#define ARR2D_VIEW_TYPE(arr2d) typeof(int(*)[(arr2d).cols])

...

Arr2D arr = array_of_smallest(42);
ARR2D_VIEW_TYPE(arr) view = arr.data;
  • Related