0

I need to write a function that returns an array of arrays:

return_array = { {1}, {1,2,3}, {...}, ....};

Each array has a different size. The function must comply to following signature:

int** generate(int n, int** column_sizes)

n is the input to function and I use that to create the return-array. I know how to create return_array, but I don't understand how size of each array should be returned in double pointer int** column_sizes?

I would just returned them in a single pointer int* column_sizes like below:

int** generate(int n, int* column_sizes){
    int return_size=some_function(n); 
    int** returned_array=malloc(return_size*sizeof(int*));
    ...
    column_sizes[0]=c0; // First array size
    column_sizes[1]=c1; // Second array size
    ...
    return returned_array;
}
doubleE
  • 1,027
  • 1
  • 12
  • 32
  • Yes it works with a single pointer. Where is the function declaration from? – Osiris Aug 30 '18 at 23:41
  • @Osiris It's something given. I can't change it. – doubleE Aug 30 '18 at 23:42
  • Other question, how do you know how many columns you created? – Osiris Aug 30 '18 at 23:43
  • 4
    I assume it is supposed to be a double pointer so you can allocate an array to put the column sizes into (*) and then pass it to the caller by a pointer (second *) – Gamification Aug 30 '18 at 23:43
  • @Pelipap Thats a very good point, since the number of arrays is obtained inside the function. – Osiris Aug 30 '18 at 23:45
  • It would help to see the code that calls your function. – user3386109 Aug 30 '18 at 23:45
  • @Pelipap Hmm why they would want to do that? It's a credible source so they should have a good reason for it. – doubleE Aug 30 '18 at 23:45
  • 1
    Because this function has to return two pieces of information: One the array containing the actual information and another array that describes how the returned array is _split up_ into sub-arrays. Such a seond return value is often realized using a pointer in C. – Gamification Aug 30 '18 at 23:50
  • 1
    @Learner As said if you determine the number of arrays inside the function it would make sense to allocate it then. Otherwise it needs to be "big enough for every case". – Osiris Aug 30 '18 at 23:51
  • 1
    "How to return array of int in pointer to pointer?" --> detail: In C _arrays_ cannot be returned. Pointers can be returned, not arrays. – chux - Reinstate Monica Aug 31 '18 at 03:34
  • 1
    `int**` is not an array, it is not a 2D array, it is not a pointer to an array and it is not a pointer to a 2D array. Thus the function signature doesn't make any sense. It seems likely that you are another victim to bad teachers/books - see [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays). – Lundin Aug 31 '18 at 10:46

2 Answers2

3

The purpose of the column_sizes parameter is to pass the number of elements in each of the sub-arrays of the returned double-pointer to the caller.

If it is to be allocated inside your function, it has to be a double pointer.

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

int** generate(int n, int** column_sizes){


    *column_sizes = malloc(n*sizeof(int));

    for (int i=0; i<n; i++)
        (*column_sizes)[i]=i;

    int** return_array=malloc(n*sizeof(*int));
    for(int i=0; i<n; i++) {
        return_array[i]=malloc((*column_sizes)[i]*sizeof(int));
        for(int j=0; j<(*column_sizes)[i]; j++) {
            // set the j'th value in the i'th array
            return_array[i][j]=i*j;
        }
    }
    return return_array;
}

int main() {
    int *column_sizes;

    int n=4;

    int** arrays= generate(n, &column_sizes);
    printf("%i\n", *column_sizes);


    for(int i=0; i<n; i++) {
        for(int j=0; j<column_sizes[i]; j++) {
            printf("%i %i: %i\n",i,j, arrays[i][j]);

        }
    }
}
Gamification
  • 787
  • 5
  • 20
  • 1
    `int** return_array=malloc(t*sizeof(int));` allocates space for `t` `int`, but `return_array` points to `int *`. Additionally, the fact the return type is `int **` rather than `int *` suggests what should be returned is not a pointer to one pointer to some `int` but rather a pointer to multiple pointers to `int`, one for each “column”. In fact, the code shown to fill the arrays conforms to that, since it uses `return_array[i][j]`, but no code is shown to allocate space for those pointers or to assign values to them. – Eric Postpischil Aug 31 '18 at 00:03
  • Hm that's a good point. your interpretation fits better, I updated my answer – Gamification Aug 31 '18 at 00:06
  • I don't think this is all correct. It must be `*(column_sizes)[0]=1;` missing parenthesis in your code. – doubleE Aug 31 '18 at 00:19
  • 1
    Yep, I thought * had precendence over [ ] and I was wrong. Tested and there's a working example now. – Gamification Aug 31 '18 at 00:37
1

There are some unresolved issues in the question, notably:

  • How are the column sizes determined?
  • How will the caller free the allocated memory?

Nonetheless, we can start answering. It looks like you need to allocate at least three spaces: One for the column sizes, one for the pointers to the columns, and one for all the actual int data. This supposes we put all the int data for all the columns in a single array but point into appropriate places in the array through the column pointers. An alternative is to allocate space for each column’s data separately.

In the former case, the function could be:

int **generate(int n, int **column_sizes)
{
    // Allocate space for columns sizes and assign column sizes.
    int NumberOfColumns = /* Some calculation not explained in question. */;
        // (size_t would be better than int, but I will use the types in the question.)
    int *sizes = malloc(NumberOfColumns * sizeof *sizes);
    // Insert code to abort if malloc failed.
    *column_sizes = sizes;
    int TotalElements = 0;
    for (int i = 0; i < NumberOfColumns; ++i)
    {
        sizes[i] = /* Some calculation to find size of column i. */;
        TotalElements += sizes[i];
    }

    // Allocate space for pointers to columns.
    int **returned_array = malloc(NumberOfColumns * sizeof *returned_array);
    // Insert code to abort if malloc failed.

    // Allocate space for the actual int data.
    int *Space = malloc(TotalElements * sizeof *Space);
    // Insert code to abort if malloc failed.

    // Assign pointers to columns.
    returned_array[0] = Space;
    for (int i = 1; i < NumberOfColumns; ++i)
        returned_array[i] = returned_array[i-1] + sizes[i-1];

    // Fill in the actual int data.
    for (int i = 0; i < NumberOfColumns; ++i)
        for (int j = 0; j < column_sizes[i]; ++j)
            returned_array[i][j] = /* Some unexplained calculation. */;

    return returned_array;
}

With this definition, the caller could free the memory by freeing the array of column sizes, freeing the space pointed to by the first pointer in the returned array, and freeing the returned array. If, in an alternative implementation, each column is allocated separately, the caller would have to free each pointer in the returned array.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • I don't understand where you've allocated memory, you've used `sizeof *sizes` and `sizeof *returned_array` and `sizeof *Space`. Can you explain please? – doubleE Aug 31 '18 at 04:50
  • 1
    @Learner: `sizeof expression` is the size of the type of the expression. If `x` is a pointer to `int`, then `sizeof *x` is the size of an `int`. Using `x = malloc(n * sizeof *x);` is preferred over `x = malloc(n * sizeof *x);` because, when you later edit the code to change what `x` is declared as, the sizes automatically remain correct; you do not have to go change all the types in `sizeof` expressions. – Eric Postpischil Aug 31 '18 at 09:53
  • Would that be allowed? Because `sizeof ` is referring to a variable that has been just defined in the same line: `type* x = malloc(sizeof *x);` – doubleE Aug 31 '18 at 17:09
  • 1
    @Learner: At the `=`, the type of `x` is complete (there is sufficient information to determine the size of objects of that type, per C 2018 6.2.5 1), and so is the type of `*x`. So at that point or after, `sizeof` may be applied to `*x`. – Eric Postpischil Sep 01 '18 at 05:08