2

I am trying to return a contiguous memory allocated array from a function but I keep on receiving errors.

Compiler returns a warning saying return from incompatible pointer type [-Wincompatible-pointer-types]

Can someone tell me what I am doing wrong?

int *test() {
    size_t rows, cols;
    // assign rows and cols
    rows = 3;
    cols = 3;
    int count = 0;

    int (*arr)[cols] = malloc(sizeof *arr * rows);
    if (arr) {
        // do stuff with arr[i][j]
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; j++) {
                arr[i][j] = count;
                count++;
            }
            /* code */
        }
    }
    return arr;
}

int main() {    
    size_t rows, cols;
    // assign rows and cols
    rows = 3;
    cols = 3;
    int count = 0;

    int (*arr)[cols] = malloc(sizeof *arr * rows);
    arr = test();

    int i = 0;
    int j = 0;

    for (i = 0; i < rows; ++i) {
        for (j = 0; j < 3; ++j)
            printf("%d ", arr[i][j]);
        printf("\n");
    }
    free(arr);
}

it should return a 2D array but returns an error and am using gcc on ubuntu

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Weez Khan
  • 137
  • 1
  • 8
  • Possible duplicate of [Return a 2d array from a function](https://stackoverflow.com/questions/8617889/return-a-2d-array-from-a-function) – MayurK Apr 05 '19 at 11:24
  • 1
    what is your compiler? – Guillaume D Apr 05 '19 at 11:25
  • @GuillaumeD most probably c99 am using gcc on ubuntu – Weez Khan Apr 05 '19 at 11:28
  • is it an error or a warning? – Guillaume D Apr 05 '19 at 11:31
  • @GuillaumeD its a warning – Weez Khan Apr 05 '19 at 11:39
  • 1
    The code `int *test()` declares `test` to return an `int *`, but `arr` is declared `int (*arr)[cols]`, so `return arr` returns an `int (*)[cols]`. There is no way in C to return a pointer to a variable length array. Instead, `test` could return a `void *`, which the caller then casts to the proper type. – Eric Postpischil Apr 05 '19 at 11:43
  • 1
    Incidentally, never declare a function in C with an empty parameter list, as in `int *test()`. That is an old style of declaration that should never be used in new code, as it lacks type-checking features. To declare a function with no parameters, use `(void)` for its parameter list. If the function takes parameters, list them, with their types, in the parameter list. – Eric Postpischil Apr 05 '19 at 11:45
  • 1
    That applies to `main` too. Do not use `int main()`. `main` should be declared with `int main(void)` or `int main(int argc, char *argv[])`, or another form that your implementation supports. – Eric Postpischil Apr 05 '19 at 11:46
  • @EricPostpischil thank you, let me have a look at that – Weez Khan Apr 05 '19 at 11:48

2 Answers2

1

Your allocation function is fine, except for a few details:

  • you should pass rows and cols as arguments
  • you should use type size_t for i and j and iterate to rows and cols instead of hard coded bounds.
  • you should use parentheses in malloc(sizeof *arr * rows); for readability: malloc(sizeof(*arr) * rows);
  • you should return &arr[0][0] or arr[0] for type correctness.

The problem is you cannot define the return type of test to be pointer to 2D array of a parametric second dimension. Hence the type error on the assignment arr = test(); cannot be fixed. You can work around this shortcoming by casting the return value to (int (*)[cols]) or simply (void *).

Here is a modified version:

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

int *test(size_t rows, size_t cols) {
    int (*arr)[cols] = malloc(sizeof(*arr) * rows);
    if (arr) {
        // initialize the matrix
        size_t count = 0;
        for (size_t i = 0; i < rows; i++) {
            for (size_t j = 0; j < cols; j++) {
                arr[i][j] = count;
                count++;
            }
            /* code */
        }
        return &arr[0][0];
    }
    return NULL;
}

int main() {
    // assign rows and cols
    size_t rows = 3;
    size_t cols = 3;

    int (*arr)[cols] = (int (*)[cols])test(rows, cols);

    if (arr) {
        for (size_t i = 0; i < rows; i++) {
            for (size_t j = 0; j < cols; j++)
                printf("%d ", arr[i][j]);
            printf("\n");
        }
        free(arr);
    }
    return 0;
}

Output:

0 1 2
3 4 5
6 7 8
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • More elegant than my answer, however, you still need a cast which converts ```int *``` into ```(int (*)[cols])```. Any ideas @EricPostpischil ? – Guillaume D Apr 05 '19 at 13:58
  • @GuillaumeD: one could avoid a cast by making `test` return a `void *`, but that's not elegant either. – chqrlie Apr 05 '19 at 14:05
  • @chqrlie thank you it has helped me, But I still dont understand the logic behind it, can you give me a reference or some source from where I can learn this. I would like to have a better understanding of it. t – Weez Khan Apr 05 '19 at 17:29
  • @WeezKhan: `test` allocates a 2D array which is essentially an array of `cols * rows* `int`s. The type `int (*arr)[cols]` specifies a mapping for this array as an array of arrays of `cols` ints. You cannot define `test` as returning this type because `cols` is a variable. Hence you can return a pointer to the first `int` and use that to initialize a pointer with the same type `int (*arr)[cols]` in `main()`, but you need a cast as shown in the example. – chqrlie Apr 05 '19 at 17:48
0

If all what you need is

to return a contiguous memory allocated array from a function

You can ignore this answer.

If what you are trying to do is to model a bidimensional container (like a matrix) using a dynamically allocated contiguous block of memory, you could define a struct and pass that around:

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

typedef struct {
    int rows, cols;
    int values[];      // I'm using a Flexible Array Member here.
} iMat;

iMat *alloc_matrix(int rows, int columns)
{
    assert(rows > 0  &&  columns > 0);
    iMat *tmp = malloc(sizeof *tmp + sizeof *(tmp->values) * rows * columns);   
    if (tmp)
    {
        tmp->rows = rows;
        tmp->cols = columns;
    }
    return tmp;
}

void fill_matrix_iota(iMat *m, int start)
{
    if ( m )
        for (size_t i = 0, n = m->rows * m->cols; i < n; ++i)
            m->values[i] = start + i;
}

void print_matrix(iMat *m, int width)
{
    if (m)
    {
        for (int i = 0, k = 0; i < m->rows; ++i)
        {
            for(int j = 0; j < m->cols; ++j, ++k)
            {
                printf("%*.d", width, m->values[k]);
            }
            putchar('\n');
        }
    }
}

iMat *make_transposed(iMat *m)
{
    if ( !m )
        return NULL;
    iMat *tmp = alloc_matrix(m->cols, m->rows);
    if ( tmp )
    {
        for (int i = 0; i < m->rows; ++i)
        {
            for(int j = 0; j < m->cols; ++j)
            {
                tmp->values[j * m->rows + i] = m->values[i * m->cols + j];
            }
        }        
    }
    return tmp;
}

int main(void)
{    
    iMat *a = alloc_matrix(3, 4);
    if (!a)
        exit(EXIT_FAILURE);

    fill_matrix_iota(a, 1);
    print_matrix(a, 3);

    iMat *b = make_transposed(a);
    if (!b)
    {
        free(a);
        exit(EXIT_FAILURE);
    }
    putchar('\n');
    print_matrix(b, 3);

    free(b);
    free(a);

    return EXIT_SUCCESS;
}
Bob__
  • 12,361
  • 3
  • 28
  • 42