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::vector
s of std::vector
s.
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);
}