0

I am trying to write a user defined function that takes some matrices and variables as inputs and gives a matrix as output. So something like this:

cofactor(int A[100][100], n, r, c){
int B[100][100]
//B becomes the cofactor matrix of A after some operations//
return B;
}

and in my main function I just want to write :

C=cofactor(D, n, r, c);

to turn C into the cofactor matrix of D.

But for some reason c language does not support taking a whole 2D array as output of a function. How can I work around this?

I don't want to keep all the junk in the main function. I want to write a separate function that gives me the matrix as output, and simply call that function in my main function.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Just declare `C` as `int **C;`. Then you can use the statement : `C=cofactor(D, n, r, c);` – Susmit Agrawal Jan 09 '19 at 06:30
  • Or... declare C with automatic storage duration (e.g. another normal 2D array) in the caller and pass C as a parameter to `cofactor` and manipulate it within the function. Declare `cofactor (int D[][100], int C[][100], n, r, c) {...}` – David C. Rankin Jan 09 '19 at 06:39
  • Why do you have an API like `int A[100][100], n, r, c`? Isn't r and c supposed to be rows and columns? But you have those set to a fixed 100. – Lundin Jan 09 '19 at 09:05

2 Answers2

1

Currently in your code B will go out of scope and will be destroyed when control exits cofactor.

Thus use pointer to pointer as below.

   int **cofactor(int A[100][100], int n, int r, int c){
        int **B = malloc(sizeof(int *)*r);

         for (int i =0;i<r;i++)
           B[i] = malloc(sizeof(int)*c);

          //B becomes the cofactor matrix of A after some operations//
          return B;
    }

And from main.

 int **C=cofactor(D, n, r, c);

Note:: NULL checks are not added and allocated memory needs to be freed once done with the processing.

kiran Biradar
  • 12,700
  • 3
  • 19
  • 44
  • 1
    "`B` will go out of scope and be destroyed" -- reference in the standard [C11 Standard - 6.2.4 Storage durations of objects](http://port70.net/~nsz/c/c11/n1570.html#6.2.4) – David C. Rankin Jan 09 '19 at 06:41
  • Why do you take an array as parameter but return a look-up table? That's a different kind of data type, this API doesn't make any sense. Check [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) – Lundin Jan 09 '19 at 08:59
  • @Lundin I agree with you,To meet OP's expectation `and in my main function I just want to write : C=cofactor(D, n, r, c);`. – kiran Biradar Jan 09 '19 at 09:00
  • They want the function to return a 2D array. This function doesn't do that. – Lundin Jan 09 '19 at 09:03
  • @Lundin Yes, I agree. But Op has constraint. – kiran Biradar Jan 09 '19 at 09:05
0

You are correct in that C doesn't allow us to return arrays from functions. This is one area where C is simply plain bad and you'll find yourself choosing between various evils. The most obvious alternatives are to return an array pointer, or a void pointer.

  • void pointers should be avoided since they have non-existent type safety.

    // bad code
    void* cofactor (int A[100][100], int n, size_t r, size_t c)
    
  • The array pointer option is rather ugly-looking, hard to read and enforces fixed-size dimensions:

    // bad code
    int ( *cofactor (int A[100][100], int n, size_t r, size_t c) )[100][100];
    
  • Alternatively, also ugly and bad practice, is to hide the array type behind a typedef:

    // bad code
    typedef int arr_t [100][100];
    
    arr_t* cofactor(int A[100][100], int n, size_t r, size_t c)
    

The array pointer versions also have the limit that you can't use variable dimensions. But r and c here seem to be rows and columns, so you probably do want the array to have variable size.

This is where some start to use int** out of confusion. But int** cannot be used to point at a 2D array, nor to the first element of a 2D array. It can be used to point at the first element of a 1D array of int* pointers, and then emulate something that looks like an array, but doesn't behave like one. That's not what you want here either, because it is both slow and dangerous. See Correctly allocating multi-dimensional arrays.

Sigh. So what to use!


If you drop the requirement of "function return ing array" (with emphasis on using return), it turns easier and more flexible. Parameter passing to/from functions in C is most often done through the parameters, and most sound APIs reserve the return value for an error type describing the outcome of the function.

The big advantage here is that when passing an array as parameter, we can use variable dimensions:

void func (size_t r, size_t c, int A[r][c])

Suddenly you can have a function accepting any array size, and somewhat type safe as long as r and c have correct values.

The cleanest is to leave allocation to the caller. Then you get

void func (size_t r, size_t c, int A[r][c], int B[r][c])

Out of all options discussed, this is the only pretty one. But it won't work if the function must do the allocation. Then we must return an array through the parameter. And to that with this syntax, turns a bit ugly too:

void copy (size_t r, size_t c, int (**B)[r][c], int A[r][c])

But if we can live with this strange-looking "pointer to array pointer to an array of int[r][c]", then it solves all problems. It can return an array of variable size from a function to the caller.

A function making a copy of any array and returning it would look like this:

void copy (size_t r, size_t c, int (**B)[r][c], int A[r][c])
{
  *B = malloc( sizeof(int[r][c]) );

  int (*b)[c] = **B; // pointer to the first row in an array int[r][c]
  for(size_t i=0; i<r; i++)
  {
    for(size_t j=0; j<c; j++)
    {
      b[i][j] = A[i][j];
    }
  }
}

Or if you will:

#include <string.h>

void copy (size_t r, size_t c, int (**B)[r][c], int A[r][c])
{
  *B = malloc( sizeof(int[r][c]) );
  memcpy( *B, A, sizeof(int[r][c]) );  
}

Full example:

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

void copy (size_t r, size_t c, int (**B)[r][c], int A[r][c])
{
  *B = malloc( sizeof(int[r][c]) );

  int (*b)[c] = **B; // pointer to the first row in an array int[r][c]
  for(size_t i=0; i<r; i++)
  {
    for(size_t j=0; j<c; j++)
    {
      b[i][j] = A[i][j];
    }
  }
}

int main (void)
{
  int array1[2][3] = { {1,2,3}, {4,5,6} };
  int (*array2)[2][3];

  copy(2, 3, &array2, array1);

  int (*arr)[3] = *array2;  
  for(size_t i=0; i<2; i++)
  {
    for(size_t j=0; j<3; j++)
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n");
  }

  free(array2);
}
Lundin
  • 195,001
  • 40
  • 254
  • 396