0

I would like to have a function which allocates a 2D array with one of the dimensions fixed and provides the allocated memory back to the caller using a pointer to an array pointer.

For example the function() would return a 2D image, where the line size is fixed but the number of lines is determined inside the function.

The arrays are defined using C99 VLA syntax, so I can access the image as a normal 2D array image[y][x] inside and outside the function.

I think function() is written mostly correctly, but I do not know how to write the code in main (array declaration). In any case I can not access the image outside the function, I get a segmentation fault and the compiler complains.

I do not wish to have an array of pointers, there should be a single malloc() and free() and all allocated memory should be continuous.

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

int function (size_t size, size_t *number, int unsigned (*image) [] [size]);

int function (size_t size, size_t *number, int unsigned (*image) [] [size]) {
    size_t image_size;

    *number = 9;
    image_size = (*number) * size;

    printf ("INSIDE malloc: number=%zu size=%zu image_size=%zu\n", *number, size, image_size); fflush(stdout);
    image = (int unsigned (*) [] [size]) malloc (sizeof(int unsigned) * image_size);

    printf ("INSIDE assign: number=%zu size=%zu\n", *number, size); fflush(stdout);
    for (int unsigned l=0; l<*number; l++) {
        for (int unsigned s=0; s<size; s++) {
            (*image) [l] [s] = (l << 16) + s;
        }
    }
    (*image) [0] [0] = 0xDEADC0DE;

    printf ("INSIDE print: number=%zu size=%zu\n", *number, size); fflush(stdout);
    for (int unsigned l=0; l<*number; l++) {
        printf ("l=%u:", l);
        for (int unsigned s=0; s<size; s++) {
            printf (" %08x", (*image) [l] [s]);
        }
        printf ("\n");
    }

    return (0);
}

int main (int argc, char **argv) {
    size_t size;
    size_t number;

    size = 5;
    int unsigned (* image) [size];

    printf ("TEST:\n"); fflush(stdout);

    function (size, &number, &image);

    fflush(stdout);
//    printf ("OUTSIDE print: number=%zu size=%zu\n", number, size); fflush(stdout);
//    for (int unsigned l=0; l<number; l++) {
//        printf ("l=%u:", l);
//        for (int unsigned s=0; s<size; s++) {
//            printf (" %08x", (*image) [l] [s]);
//        }
//        printf ("\n");
//    }

//    free(image);

    return (0);
}

I am compiling with clang and gcc and getting the next warning:

clang -o test test.c -g -std=c99
test.c:44:34: warning: incompatible pointer types passing 'unsigned int (**)[size]' to parameter of type 'unsigned int (*)[][*]' [-Wincompatible-pointer-types]
        function (size, &number, &image);
                                 ^~~~~~
test.c:6:63: note: passing argument to parameter 'image' here
    int function (size_t size, size_t *number, int unsigned (*image) [] [size]) {
                                                              ^
1 warning generated.

Actually my problem has one dimension more than the given example (list [i] [y][x]) but the same solution should apply.

  • `malloc (sizeof(int unsigned) * image_size);` That is not how to allocate a 2D array. – kaylum Sep 21 '16 at 22:53
  • 1
    Possible duplicate of [Allocate memory 2d array in function C](http://stackoverflow.com/questions/15062718/allocate-memory-2d-array-in-function-c) – kaylum Sep 21 '16 at 22:53
  • @Iztok [sample code](http://ideone.com/B39d3W) – BLUEPIXY Sep 21 '16 at 23:12
  • A pointer is not an array and vice-versa. Any reason you think the types of the pointer you pass and the type of the paramter for it should differ? – too honest for this site Sep 22 '16 at 00:31
  • Possible duplicate of [Passing a 2D array of structs](http://stackoverflow.com/questions/35614862/passing-a-2d-array-of-structs) – too honest for this site Sep 22 '16 at 00:32
  • warning says it all, the function accepts a pointer to 2d array, but your `image` in `main` function is a pointer to 1d array, the address of which is a pointer to a pointer to 1d array. so obviously they are incompatible. – user3528438 Sep 22 '16 at 01:03
  • `image = (int unsigned (*) [] [size]) malloc (sizeof(int unsigned) * image_size);` - please explain that monstrosity before `malloc` – M.M Sep 22 '16 at 02:12
  • @Olaf the suggested duplicate doesn't cover having the function allocate the array – M.M Sep 22 '16 at 02:39
  • C does not support references! – too honest for this site Sep 22 '16 at 13:15
  • @Olaf Please learn about "pass by reference". Here is an entry from the [comp.lang.c wiki](http://clc-wiki.net/wiki/C_language:Terms:Pass_by_reference). – M.M Sep 22 '16 at 21:14

1 Answers1

-1

I'm assuming your requirements are:

  • The function "returns" a pointer-to-VLA via an out-parameter.
  • Caller does not know the number of rows beforehand; the function decides it and "returns" it via out-parameter.

The code could look like:

#include <stdlib.h>

void init(int rows, int cols, unsigned arr[rows][cols])   // same as "unsigned (*arr)[cols]"
{
   for (int r = 0; r < rows; ++r)
        for (int c = 0; c < cols; ++c)
             arr[r][c] = r*c;       
}

void func(int *p_rows, int cols, unsigned (**p_arr)[][cols])
{
   *p_rows = 7;
   *p_arr = malloc( sizeof(unsigned[*p_rows][cols]) );

   if ( !*p_arr )
       exit(EXIT_FAILURE);

   // Possible to initialize here but it makes for more readable code to use a separate function
   // (**p_arr)[2][3] = 5;

   init(*p_rows, cols, **p_arr);
}


int main()
{
   int cols = 7;
   int rows;

   unsigned (*p_arr)[][cols];
   func(&rows, cols, &p_arr);

   // Could do this here instead of calling init() from func()
   init(rows, cols, *p_arr);
   (*p_arr)[2][3] = 5;

   // Optional - allow simpler access syntax
   unsigned (*arr)[cols] = (void *)p_arr;
   init(cols, rows, arr);  
   arr[2][3] = 5;
}

NB. It would simplify the code if you made a separate function for getting the rowcount than for doing the allocation. It would also simplify the code to return the allocated pointer, instead of using an out-parameter.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • thanks M.M this was a great answer, it works as advertised – Iztok Jeras Sep 22 '16 at 18:10
  • What I still do not understand is why the double pointer to an array. I thought the array itself was a pointer so only one * would be needed to implement referencing. While debugging this issue I already learned parentheses are needed because [] has priority, but the above still bothers me. Pointers are far from magic, but the syntax still makes me question my intelligence. – Iztok Jeras Sep 22 '16 at 18:20
  • @IztokJeras arrays and pointers are different. An array is not a pointer. An array is a block of contiguous storage. A pointer is something that indicates whereabouts in memory another object can be found. – M.M Sep 22 '16 at 21:11
  • Further to that: A named array cannot be modified or resized once it has been declared. So it is not possible to declare a named array in `main` and then pass something to `func` to allocate the array. The only option is that the array is dynamically allocated, and in `main` there is a named pointer that points to either the entire array, or the array's first row (those are the two different cases I gave examples of in my answer). Then, since `func` needs to modify that pointer declared in `main`, it must pass a pointer to that pointer. (Unless you use the return value instead) – M.M Sep 22 '16 at 21:27