2

I have the following function:

int* create_matrix_2(int rows, int cols)
{
    double (*A)[rows][cols] = malloc(sizeof(int[rows][cols]));

    for (int row = 0; row < rows; row++)
    {
        for (int col = 0; col < cols; col++)
        {
            *A[row][col] = row * cols + col;
        }
    }

    for (int row = 0; row < rows; row++)
    {
        for (int col = 0; col < cols; col++)
        {
            printf("%lf, " , *A[row][col]);
        }
        printf("\n");
    }
    return A;
}

My question is: How do I return the VLA from that function, what is the type and how do I write that as a function signature and how do I use it where I receive the returned array?

At the moment I am trying to do it like this:

int (*matrix2)[height][width] = create_matrix_2(height, width);

for (int row = 0; row < height; row++)
{
    for (int col = 0; col < width; col++)
    {
        printf("%d" , (*matrix2)[row][col]);
    }
    printf("\n");
}

and then run: gcc 2d_array_test.c -o 2d_array_test.out -std=c99 -O0

But this results in the following problems:

2d_array_test.c: In function ‘main’:
2d_array_test.c:35:34: warning: initialization from incompatible pointer type [enabled by default]
  int (*matrix2)[height][width] = create_matrix_2(height, width);
                                  ^
2d_array_test.c: In function ‘create_matrix_2’:
2d_array_test.c:105:2: warning: return from incompatible pointer type [enabled by default]
  return A;
  ^

EDIT#1:

I tried to use the code suggested by alk, but it gives me a lot of errors while compiling. Here is a separate programm, which contains your suggested code with a main function: http://pastebin.com/R6hKgvM0 I get the following errors:

2d_array_test_new.c: In function ‘main’:
2d_array_test_new.c:18:2: warning: passing argument 3 of ‘create_matrix’ from incompatible pointer type [enabled by default]
  if (-1 == create_matrix(height, width, &matrix))
  ^
2d_array_test_new.c:10:5: note: expected ‘int **’ but argument is of type ‘int (**)[(sizetype)(height)][(sizetype)(width)]’
 int create_matrix(size_t, size_t, int**);
     ^
2d_array_test_new.c: At top level:
2d_array_test_new.c:37:5: error: conflicting types for ‘create_matrix’
 int create_matrix(size_t rows, size_t cols, int(**a)[rows][cols])
     ^
2d_array_test_new.c:10:5: note: previous declaration of ‘create_matrix’ was here
 int create_matrix(size_t, size_t, int**);
     ^
2d_array_test_new.c: In function ‘create_matrix’:
2d_array_test_new.c:45:11: error: ‘EINVAL’ undeclared (first use in this function)
   errno = EINVAL;
           ^
2d_array_test_new.c:45:11: note: each undeclared identifier is reported only once for each function it appears in
2d_array_test_new.c:40:6: warning: variable ‘errno’ set but not used [-Wunused-but-set-variable]
  int errno;
      ^

The errors mainly seem to be about the return type. How do I write the type of that array correctly?

Zelphir Kaltstahl
  • 5,722
  • 10
  • 57
  • 86
  • 1
    `*A[row][col]` - `[]` has higher precedence than `*`; this is probably not doing what you want it to. – Alex Celeste May 25 '15 at 12:20
  • @KlasLindbäck I see that, the question title is confusing. – Iharob Al Asimi May 25 '15 at 12:21
  • 1
    You allocate memory for `int[x][y]` to a pointer to `double[x][y]`. Why? – alk May 25 '15 at 12:23
  • 2
    Also you want to use `(*A)[i][j]`. – alk May 25 '15 at 12:24
  • The safe option is, sadly, to just don't use VLA's: C11 made VLA support optional, and C99 VLA's are strictly block-scoped. – Elias Van Ootegem May 25 '15 at 12:55
  • That is no VLA! However, they still have their application and I would not discourace from using them; but just give the hint. Still wonder why they mad a language construct optional after 12 years. – too honest for this site May 25 '15 at 14:26
  • @Olaf While `A` itself is not a VLA, the above code depends on the VLA feature being available so that `int[rows][cols]` can be a valid type. – Alex Celeste May 25 '15 at 16:18
  • 1
    @Leushenko: thank you make me read the standard. You are right, `*A` actually _is_ a VLA. Strange usage anyway, as one always has to pass the dimensions explicitly along. – too honest for this site May 25 '15 at 16:38
  • Ah, I didn't see that I was using double -.- I was so focused on the return type and the asterisks etc ... thanks for pointing it out! – Zelphir Kaltstahl May 25 '15 at 17:36
  • Regarding your edit: The prototype to `create_matrix()` is different from it's implementation. The 3rd and 4th error/note listed by your edit clearly states this: `conflicting types for ‘create_matrix’` ...`note: previous declaration of ‘create_matrix’ was here`. Sry, but do you even read those errors/warnings? – alk May 26 '15 at 18:03
  • To have `EINVAL` and `errno` include `errno.h`. – alk May 26 '15 at 18:05
  • Do not define `errno` yourself. Include `errno.h` to have it. – alk May 26 '15 at 18:21
  • You might like to read `man errno` and `man 3 perror`. – alk May 26 '15 at 18:27
  • I am sorry, I read the warnings, but even knowing what the warning means, I don't know how to correct the signature / prototype of the function, since it's not clear to me how to write that type (syntactically). I was not sure what EINVAL actually does and I didn't know errno.h nor did I know perror. I thought "perror will print something in a different color when there is an error at that point." and since that didn't happen, I thought it was not of importance at that moment. -- I appreciate your help though. – Zelphir Kaltstahl May 26 '15 at 18:45

4 Answers4

2
  • refering the 1st warning:

    warning: initialization from incompatible pointer type
    

    Here

    int (*matrix2)[height][width] = create_matrix_2(height, width);
    

    int (*matrix2)[height][width] and int * simply aren't the same.

  • refering the 2nd warning:

    warning: return from incompatible pointer type 
    

    This dues to defining

    double (*A)[rows][cols] = malloc(sizeof(int[rows][cols]));
    

    while returning A from int * create_matrix_2().

    int * and double (*A)[rows][cols] also aren't the same.

I propose you change the function like this:

#include <errno.h> /* for errno and EINVAL */
#include <stdlib.h> /* for malloc() */


int create_matrix_2(size_t rows, size_t cols, int(**a)[rows][cols])
{
  int result = 0;

  if (NULL == a)
  {
    result = -1;
    errno = EINVAL;
  }
  else
  {
    (*a) = malloc(sizeof **a);
    if (NULL == (*a))
    {
      result = -1;
    }
    else
    {
      for (size_t row = 0; row < rows; row++)
      {
        for (size_t col = 0; col < cols; col++)
        {
          (**a)[row][col] = row * cols + col;
        }
      }

      for (size_t row = 0; row < rows; row++)
      {
        for (size_t col = 0; col < cols; col++)
        {
          printf("%d, " , (**a)[row][col]);
        }

        printf("\n");
      }
    }
  }

  return result;
}

and call it like this:

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


int create_matrix_2(size_t rows, size_t cols, int(**a)[rows][cols]);


int main(void)
{
  int result = EXIT_SUCCESS;

  int (*matrix2)[height][width] = NULL;
  if (-1 == create_matrix_2(height, width, &matrix2))
  {
    perror("create_matrix_2() failed");
    result = EXIT_FAILURE;
  }
  else
  {
    for (size_t row = 0; row < height; row++)
    {
      for (size_t col = 0; col < width; col++)
      {
        printf("%d, " , (*matrix2)[row][col]);
      }

      printf("\n");
    }

    free(matrix2);
  }

  return result;
}
alk
  • 69,737
  • 10
  • 105
  • 255
  • What's the terminology used by the standard for using function arguments as part of the type of another argument like that? – Flexo May 25 '15 at 12:40
  • `create_matrix_2` does not return a value. – BLUEPIXY May 25 '15 at 12:48
  • @BLUEPIXY: The function modifies the pointer pointed to by the last parameter. – alk May 25 '15 at 12:50
  • 1
    ha! see `int create_matrix_2(...` and `if (-1 == create_matrix_2(...` – BLUEPIXY May 25 '15 at 12:53
  • @Flexo: "*using function arguments as part of the type of another argument*" I am not aware of any special naming for this. However a variable like `int (**a)[rows][cols]` (as part of a function declaration) is referred to by the C-Standard as "*V(ariably)M(odified) parameter*" (C11 Draft 6.7.6.3/20). – alk May 25 '15 at 13:17
  • alk, @flexo: `**a` would be a variable length array (VLA, §6.7.6.2) – too honest for this site May 25 '15 at 17:02
  • "V(ariably)M(odified) parameter" was what I was looking for - I knew I'd seen it expressed somewhere. – Flexo May 25 '15 at 17:05
  • @Zelphir: I added some clarifing details. – alk May 26 '15 at 18:30
  • The whole `(*varname)` or `(** varname)` business is still a bit confusing, but I guess it's just a matter of time until it becomes easier to write. This solution is working. One of the traps is, that `&(**varname)[y][x]` is not the same as `(*varname)[y][x]` for example. Had some issues with scanf, which expects the address of the variable in which to write, but those are solved now as well. Thank you for your help. – Zelphir Kaltstahl May 27 '15 at 07:19
  • Yeah, and I guess `[]` has the higher precedence than `&` since you can leave the brackets away too. – Zelphir Kaltstahl May 27 '15 at 07:21
2

First, there are some issues with your code. You have a double array pointer pointing to a 2D array of ints, that doesn't make any sense.

Also, since A is a pointer to a 2D array, you can't do *A[row][col]. Because [] has higher precedence than *, so the code is equivalent to *(A[row][col]). Which is turn means "give me row number of 2D matrices, then...". The code will end up far out of bounds.

The trick when declaring an array pointer to a dynamically allocated multi-dimensional array is to omit the inner-most dimension. That way you can use syntax just like you would with a regular multi-dimensional array.

double (*A)[cols] = malloc(sizeof(double[rows][cols]));
...
A[x][y] = ...;

A[x][y] will then mean "In my array A, where each item is an array of size cols, access array number x and then index y in that array".


As for the question of how to return an array of this type... well you can't, because the function declaration would have to contain the dimensions of the array pointer. It would have to be a monstrosity like this:

double (*create_matrix_2(int rows, int cols))[rows][cols]   // wont compile

If we ignore that the C syntax for returning array pointers (and function pointers) is completely FUBAR, the above isn't valid C. Because rows and cols would then have to be known at compile time.

The solution to clearing up the above mess and fixing the issue at the same time is to return the array pointer through a parameter:

void create_matrix_2 (int rows, int cols, double(**array)[rows][cols])
{
    // omit inner-most dimension to get sane syntax:
    double (*A)[cols] = malloc(sizeof(double[rows][cols]));

    for (int row = 0; row < rows; row++)
    {
        for (int col = 0; col < cols; col++)
        {
            A[row][col] = row * cols + col;
        }
    }

    for (int row = 0; row < rows; row++)
    {
        for (int col = 0; col < cols; col++)
        {
            printf("%lf, " , A[row][col]);
        }
        printf("\n");
    }

    // "brute" pointer conversion but doesn't break aliasing:
    *array = (void*)A;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • However, the sane solution here might be to simply pass the array as parameter and leave the allocation to the caller instead. `void create_matrix_2 (int rows, int cols, double array[rows][cols])` – Lundin May 25 '15 at 13:21
  • That will not work: `A[x][y]` is identical to `A + x * cols + y`. But A has only one dimension, so the compiler does not know about `cols`. – too honest for this site May 25 '15 at 13:23
  • @Olaf It will work because the first [] is pointer arithmetic on array level, second [] is array indexing of the pointed-at array. – Lundin May 26 '15 at 06:29
  • Maybe I'm currently a bit brain-dead, but : Would that not require `double *(*A)[cols]` and the pointers properly intialized? It would also require `malloc(sizeof(...) + sizeof(double *) * cols)` – too honest for this site May 26 '15 at 12:24
  • @Olaf If you for example have a pointer to a char, `pointer[]` gives you a char object. Similarly, if you have a pointer to an array, `pointer[]` gives you an array object. `pointer[][]` in turn gives you an item index in that array. – Lundin May 26 '15 at 12:33
  • Ok, got it. I'm really a byte (8 bit) slow today. As I might not be the only one, at least the comments might help others, too. Anyway, that is something I try to avoid in C (and don't need in other languages), exaclty for this reason. Most times there are easier-to-understand and (often) better - for the whole program - approaches. – too honest for this site May 26 '15 at 12:57
1

The short answer is that you can't.

The problem is that the type returned from a function must be fixed at compile time and cannot depend on the numeric value (supplied at run time) of its parameters.

What you can do, however, is change your create_matrix2() to return a void pointer. This uses the fact that, in C, (almost) any pointer can be implicitly converted to a void pointer, and vice versa.

   void *create_matrix2(int rows, int columns)
   {
       /* as you've implemented it, except that A should be int not double */
   }

   int main()
   {
        int rows = 2, cols = 3;
        int (*m)[rows][cols] = create_matrix2(rows, cols);

         /*  use *m as a pointer to an array with 2 rows and 3 cols */
   }

It then delivers the illusion of what you seek.

The danger of this - and the reason I use the word "illusion" just now - is that the conversion to and from a void pointer prevents the compiler doing type checking. So this will sail past the compiler, but cause undefined behaviour due to falling off the end of arrays.

   int main()
   {
        int rows = 2, cols = 3;
        int (*m)[rows][cols] = create_matrix2(rows-1, cols-1);   /*  whoops - deliberate typos here */

         /*  use *m as a pointer to an array with 2 rows and 3 cols */
   }

because this effectively tells the compiler to treat the dynamically allocated array as if it has more rows and columns than actually allocated in the function.

Peter
  • 35,646
  • 4
  • 32
  • 74
0

sample

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

void * create_matrix_2(int rows, int cols){
    int (*A)[rows][cols] = malloc(sizeof(int[rows][cols]));

    for (int row = 0; row < rows; row++){
        for (int col = 0; col < cols; col++){
            (*A)[row][col] = row * cols + col;
        }
    }

    for (int row = 0; row < rows; row++){
        for (int col = 0; col < cols; col++){
            printf("%d, " , (*A)[row][col]);
        }
        printf("\n");
    }
    return A;
}

int main(void){
    int width = 5, height = 3;
    int (*matrix2)[height][width] = create_matrix_2(height, width);

    for (int row = 0; row < height; row++){
        for (int col = 0; col < width; col++){
            printf("%d " , (*matrix2)[row][col]);
        }
        printf("\n");
    }
    free(matrix2);
    return 0;
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70