2

I know this question has been asked, but I haven't really seen an answer that makes sense to me.

I'm writing a function to calculate determinants in C++. To do this, I need to pass a matrix.

My code looks like the following:

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

double determinant(int N, double M[N][N]);

int main(){
    double myMatrix[3][3] = {{1,2,3}, {4,5,6}, {3,6,1}};
    double okay = determinant(4, myMatrix);
    printf("determinant = %f\n", okay);
}


double determinant(int N, double M[N][N]){
    double det=0.0;
    //if N=2 matrix, use the easy formula
    if(N==2){
        det = M[0][0]*M[1][1] - M[0][1]*M[1][0];
    }
    //otherwise, find minor matrices, multiply by top row
    else {
        double Mminor[N-1][N-1];
        double sign = 1.0;
        for(int i=0; i<N; i++){
            //get the minor matrix
            for(int a=1;a<N;a++){
                int m=0;
                for(int b=0;b<N;b++){
                    if(b!=i){
                        Mminor[a-1][m] = M[a][b];
                        m++;
                    }
                }
            }
            //add top row element times determinant of its minor
            det += sign*M[0][i]*determinant(N-1, Mminor);
            //swap the sign
            sign *= -1.0;
        }
    }
    return det;
}

If I compile this as C and use the C99 compiler, it runs fine and with no issues, giving the correct values for several different test matrices.

But I don't need this to compile as C, I need it to compile as C++. And it will not compile as C++. The GNU compiler gives the following errors:

determinant.c:25:24: error: no matching function for call to 'determinant'
                        det += sign*M[0][i]*determinant(N-1, Mminor);
                                            ^~~~~~~~~~~
determinant.c:5:8: note: candidate function not viable: no known conversion from 'double [N - 1][N - 1]' to
      'double (*)[N]' for 2nd argument
double determinant(int N, double M[N][N]){
       ^
determinant.c:36:16: error: no matching function for call to 'determinant'
        double okay = determinant(4, myMatrix);
                      ^~~~~~~~~~~
determinant.c:5:8: note: candidate function not viable: no known conversion from 'double [4][4]' to 'double (*)[N]'
      for 2nd argument
double determinant(int N, double M[N][N]){

I know that C and C++ have a deep prejudice against 2D arrays and don't like them and refuse to work with them, but everything I've read has suggested that by specifying the size N as an argument that it should work. And it does. In C99. But not with the g++ compiler (neither C++99 nor C++11).

Is there some specific compiler flag I have to use? Or some other way to do this?

To be clear: I need the size of the matrix to be flexible, because it's going to be different every time I call the function.

I'd like to avoid using 1D arrays to make 2D arrays, if at all possible. I know how to do this with 1D arrays, I'd just rather do this in a more natural and elegant way.

Thank you.

Edit: And I know you're going to suggest I use vector<> instead. Using vector<> isn't optimal because this all is supposed to go into a much larger program at around 20,000 lines that only uses arrays.

Edit 2: The GNU compiler documentation says that it implements VLAs in C++ as an extension of the language. Several other past posters have posted their surprise that VLAs work and asking how to turn the functionality off. But how does one turn it on?

RBoston
  • 33
  • 6
  • Write a simple matrix class if you can't/don't want to use an external library like `Eigen`. As a side note, the way you calculate a determinant is very inefficient. Do not do it in real life unless your matrix is really small. – Evg Aug 05 '18 at 06:59
  • 1
    You're right, we are going to sugest you to use `vector`s, to see why we don't have _Variable Length Arrays_ in c++: https://stackoverflow.com/questions/1887097/why-arent-variable-length-arrays-part-of-the-c-standard – David Ranieri Aug 05 '18 at 07:02
  • 1
    Or, put another way, it will compile and run on C99 because C provides a VLA. There is no such animal in C++ -- thus your problem. – David C. Rankin Aug 05 '18 at 07:05
  • Matrix class may be it, I guess :/ – RBoston Aug 05 '18 at 07:06
  • You would think C++ would implement VLA before it bothered with lambda functions – RBoston Aug 05 '18 at 07:07
  • The [GNU compiler documentation](http://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html) says that it offers VLA as an extension to C90 and C++, but I'm not sure how to unlock it – RBoston Aug 05 '18 at 07:11
  • No, no. You can simply `#define NDIM 3` and then `double determinant(int N, double M[NDIM][NDIM])` and `double Mminor[NDIM][NDIM];` and then compile with `g++`. It is the fact you were using non-constant indexes that were causing the C++ issue. You can use `vector`, etc.., but you can also simply use constant indexes. (now if you need to pass a variable number of indexes -- then you must use the C++ vector niceties -- or dynamically allocate your storage) – David C. Rankin Aug 05 '18 at 07:12
  • I could do that, but `NDIM` changes, since it gets called recursively on a submatrix. So first it gets called on a 5x5 matrix, then on 4x4 submatrices, then on 3x3 submatrices, etc. ~~I think I'm just going to copy/paste the function body into the code where I need to use it.~~ that won't work because its recursive >. – RBoston Aug 05 '18 at 07:26
  • What is `double determinant(int N, double M[N][N])`? – Amit G. Aug 05 '18 at 07:31
  • @RBoston Lambda functions are far more useful than VLAs IMHO. If you prefer arrays then maybe you're a C programmer at heart. – john Aug 05 '18 at 07:41
  • I think I'm more of a "C+" guy. I make extensive use of OOP. I just never use the library, even when I can't avoid it. Amit - it's a forward declaration of the function; the definition is below main() – RBoston Aug 05 '18 at 07:45
  • IMO life will be easier if you avoid C-style arrays in C++ – M.M Aug 05 '18 at 08:55

1 Answers1

2

In C++, you might use template:

#include <iostream>

double determinant(const double (&M)[2][2]){
    return M[0][0] * M[1][1] - M[0][1] * M[1][0];
}

template <std::size_t N>
double determinant(const double (&M)[N][N]){
    double det=0.0;

    double Mminor[N - 1][N - 1];
    double sign = 1.0;
    for (int i = 0; i < N; i++) {
        //get the minor matrix
        for (int a = 1; a < N; a++) {
            int m=0;
            for (int b = 0; b < N; b++) {
                if (b != i) {
                    Mminor[a - 1][m] = M[a][b];
                    m++;
                }
            }
        }
        //add top row element times determinant of its minor
        det += sign * M[0][i] * determinant(Mminor);
        //swap the sign
        sign *= -1.0;
    }
    return det;
}

int main(){
    const double myMatrix[3][3] = {{1,2,3}, {4,5,6}, {3,6,1}};
    double okay = determinant(myMatrix);
    std::cout << "determinant = " << okay << std::endl;
}

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302