0

I am trying to get an understanding of how to work with matrices in C++. The code at the bottom is supposed to take an input matrix and return the places where there are 0s. However, I am getting the following errors:

 matrix.cpp:47:3: error: no matching function for call to 'make_zero'  make_zero(i,j,l);
     ^~~~~~~~~
    matrix.cpp:8:6: note: candidate function not viable: no known conversion from 'double [i][j]' to
      'double (*)[col]' for 3rd argument
    void make_zero(int row, int col, double matrix[row][col])
     ^
    1 error generated.

when I try to run the following code:

// Matrix

#include <iostream> 
#include <stdio.h>

using namespace std;

void make_zero(int row, int col, double matrix[row][col])
  {
     int k,l;  
 for(k=0;k<row;k++)
    for(l=0;l<col;l++)
    {
        if(matrix[k][l]==0)
           printf("%d %d\n",k,l);
    }

 }



int main ()
{
  int i = 0,j = 0;

  cout << "Enter no of rows of the matrix";
  cin >> i;
  cout << "Enter no of columns of the matrix";
  cin >> j;

  double l[i][j];

  int p = 0, q = 0;

  while (p < i) {
while (q < j) {
  cout << "Enter the" << p + 1 << "*" << q + 1 << "entry";
  cin >> l[p][q];

  q = q + 1;
}
p = p + 1;
q = 0;
  }
  cout << l << "\n";

  make_zero(i,j,l);
}

Any help would be appreciated. Thanks.

  • c or c++ ? text says c, but I guess you meant c++ – 463035818_is_not_an_ai Jan 13 '16 at 18:52
  • 2
    `double l[i][j];` VLA's aren't standad c++. Use `std::vector` instead. – πάντα ῥεῖ Jan 13 '16 at 18:54
  • Why don't you use a vector of vectors instead of a two-dimensional array? Check [this](https://github.com/ForceBru/Matrix) out of you want an example of how to code a simple C++ library to work with matrices. – ForceBru Jan 13 '16 at 18:55
  • You cannot provide the size in a function parameter (at least as far as I remember), use `double matrix[][]` or `double **matrix` – SGM1 Jan 13 '16 at 19:05
  • @πάνταῥεῖ I understand `vector` is the standard in C++, but I don't believe there is anything against the standard to use VLA's? – SGM1 Jan 13 '16 at 19:09
  • @SGM1 VLA's aren't supported by the current c++ standard, but just by some compiler specific extensions. So they aren't standard, period. – πάντα ῥεῖ Jan 13 '16 at 19:12
  • For those interested in the standard 's discussion: http://stackoverflow.com/questions/1887097/variable-length-arrays-in-c – SGM1 Jan 13 '16 at 19:23
  • OT, but you shouldn't mix `iostream` and `stdio`, pick one! – Bob__ Jan 13 '16 at 19:45
  • I suggest you to take a deep look into this: http://stackoverflow.com/questions/4810664/how-do-i-use-arrays-in-c – Bob__ Jan 13 '16 at 19:57

2 Answers2

1
void make_zero(int row, int col, double ** matrix)

Note, that you need to pass also size of the matrix separately.

Also you can use

std::vector<std::vector<double> >

instead and pass this object by reference, pointer, or just make a copy.

Actually, it works, but your problem in this line also:

double l[i][j];

i, j is unknown during the compile time. You have 2 ways.

1) dynamically allocate the memory

2) use std::vector<std::vector<double> >. Default constructor already sets zero values. But you can do it manually like this:

#include <iostream>
#include <vector>

void make_zero(std::vector<std::vector<double> > & to_zero) {
    for (int i = 0; i < to_zero.size(); ++i) {
        for (int j = 0; j < to_zero[i].size(); ++j) {
            to_zero[i][j] = 0;
        }
    }
}


void print_double_vector(const std::vector<std::vector<double> > & to_print) {
    for (int i = 0; i < to_print.size(); ++i) {
        for (int j = 0; j < to_print[i].size(); ++j) {
            std::cout << to_print[i][j] << " ";
        }
        std::cout << std::endl;
    }
    std::cout << std::endl;
}


int main() {
    // your code goes here
    int n, m;
    std::cin >> n >> m;
    std::vector<std::vector<double> > d(n, std::vector<double>(m));
    print_double_vector(d);
    make_zero(d);
    print_double_vector(d);
    return 0;
}

http://ideone.com/0X53Yj

SashaMN
  • 708
  • 5
  • 16
  • void make_zero(int row, int col, double ** matrix) doesn't seem to work. Can you please elaborate on using std::vector, or point me in the right direction? Thanks. – Syed Rahman Jan 13 '16 at 19:10
  • problem with this answer: `double l[i][j]` does not decay to `double**` even if the compiler supports VLA. Something worth noting: `std::vector >` has serious performance problems due to the lack of contiguity between the `vector`s in the outer `vector`. Recommend using a 1D vector instead and doing the 2D indexing manually. – user4581301 Jan 13 '16 at 19:16
  • @SyedRahman take a look at http://stackoverflow.com/questions/34077816/how-to-properly-work-with-dynamically-allocated-multi-dimensional-arrays-in-c – user4581301 Jan 13 '16 at 19:22
1

There are a bunch of ways to do this with pointers. The most common is

void make_zero(int row, int col, double ** matrix)

defines a pointer (usually rows) to a pointer (usually columns). Unfortunately

double l[i][j]; 

does not define a pointer to a pointer. If this syntax is supported by the compiler, and the compiler is not required to allow arrays of variable length, it most likely defines a pointer to a 1D array (double l[i*j];) and hides the indexing arithmetic used to convert the array to two dimensions. Anyway, it can't be passed to a double ** because it isn't a double **

Trying to pass as an array is troublesome

void make_zero(int row, int col, double matrix[][NUMBER_OF_COLUMNS])

The number of columns in the array must be known to perform the indexing arithmetic and be provided to any functions called with it. This means that number of columns cannot be changed at run time because the indexing used by the function will be rendered invalid.

Getting around this would require changes to the compiler that will drive it further and further from the C++ standard. A bad idea since there are a number of simple ways around calling functions with multi dimensional arrays. Most depend on arrays of arrays or std::vectors of std::vectors.

And when it comes to these solutions, as far as I'm concerned, the best is don't. I'm not going to cover them.

None of the arrays representing a dimension are guaranteed to be anywhere close to the others in memory, and this limits the CPU's ability to read and cache. Without caching and being able to look ahead, a modern CPU is at a serious performance disadvantage. (Read for more information: Why is it faster to process a sorted array than an unsorted array?)

So what you want is a 1 D array, and those are easy to pass around. The indexing math is also easy, row number * size of column + column number, but you need to pass at least the size of the column around. Rather than scatter the book-keeping around like this:

void make_zero(int row, int col, std::vector<double> matrix)

make a wrapper class like this:

class Matrix
{
private:
    std::vector<double> myArray;
    size_t nrRows;
    size_t nrColumns;

public:
    Matrix(size_t rows, size_t columns) :
            myArray(rows * columns), // allocate vector to store matrix. 
            nrRows(rows), 
            nrColumns(columns)
    {

    }
    size_t getNrRows() const
    {
        return nrRows;
    }
    size_t getNrColumns() const
    {
        return nrColumns;
    }

    // gets value at row, column and returns a reference so caller can 
    // modify the value
    double& operator()(size_t row, size_t column)
    {
        // note: No sanity check for row >= nrRows or column > nrColumns
        return myArray[row * nrColumns + column];
    }

    // gets value at row, column and returns a copy so caller cannot
    // change the contents of the Matrix
    double operator()(size_t row, size_t column) const
    {
        return myArray[row * nrColumns + column];
    }
};

Using the vector gets around a number of common pointer-to-array problems by managing its own memory. No destructor is required and Matrix can be copied and moved without requiring special handling because vector performs all that heavy lifting for us.

And as a usage example, let's make a function that prints the matrix out:

std::ostream & operator<<(std::ostream &  out, const Matrix & in)
{
    for (size_t i = 0; i < in.getNrRows(); i++)
    {
        for (size_t j = 0; j < in.getNrColumns(); j++)
        {
            out << in(i,j) << ' ';
        }
        out << "\n";
    }

    return out;
}

And modifying OP's main function to use Matrix we get:

int main()
{
    int i = 0, j = 0;

    cout << "Enter no of rows of the matrix";
    cin >> i;
    cout << "Enter no of columns of the matrix";
    cin >> j;

    Matrix matrix(i,j);

    int p = 0, q = 0;

    while (p < i)
    {
        while (q < j)
        {
            cout << "Enter the" << p + 1 << "*" << q + 1 << "entry";
            cin >> matrix(p,q);

            q = q + 1;
        }
        p = p + 1;
        q = 0;
    }
    cout << matrix << "\n";

    make_zero(matrix);
}
Community
  • 1
  • 1
user4581301
  • 33,082
  • 7
  • 33
  • 54