3

Basically, my problem is: I have the user to define the size (N,M) of a 2D array and then I declare:

int matrix[N][M];

then I need to pass this uninitialized matrix to a function that reads some data from a .csv file and puts it into the matrix, so I tried:

void readwrite(int &matrix[N][], const int N, const int M){....};


int main(){
....
cin>>N;
cin>>M;

int matrix[N][M];
readwrite(matrix,N,M);
};

However, when i compile it, it gives me the following error: "N was not declared in this scope".

Any ideas of how to make this work?

Thank y'all!

mariob6
  • 379
  • 5
  • 13
  • 2
    Off topic: `int matrix[N][M];` with variable `N` and `M` is not legal C++. It is allowed by extension in some compilers. Be wary and don't expect it to work when porting to different compilers. – user4581301 Dec 14 '15 at 23:01
  • Maybe this will help? http://stackoverflow.com/questions/936687/how-do-i-declare-a-2d-array-in-c-using-new – Mark Ransom Dec 15 '15 at 01:21

2 Answers2

1

What The OP is trying is so annoyingly difficult to get right and the benefits of pulling it off are so minuscule compared to the costs that... Well, I'll quote from the Classics.

The only winning move is not to play.

-Joshua, WarGames

You cannot safely pass a dynamically allocated 2D array in C++ into a function because you always have to know at least one dimension at compile time.

I could point over at Passing a 2D array to a C++ function because that looks like a good duplicate. I won't because it's referring to statically allocated arrays.

You can play silly casting games to force the array into the function, and then cast it back on the inside. I'm not going to explain how to do this because it is epic-class stupid and should be a firing offense.

You can pass a pointer to a pointer, int **, but the construction and destruction logic is a grotesque set of new and loops. Further, the end result scatters the allocated memory around the RAM, crippling the processors attempts at prediction and caching. On a modern processor if you can't predict and cache, you are throwing away the greater part of your CPU's performance.

What you want to do is stay one dimensional. A 1D array is easy to pass. The indexing arithmetic is dead simple and easy to predict. It's all one memory block so cache hits are more likely than not.

Making a 1D array is simple: Don't. Use std::vector instead.

std::vector<int> arr(rows*columns);

If you have to because the assignment spec says "No Vectors!" Well you're stuck.

int * arr = new int[rows*columns];

Note I'm using rows and columns not M and N. When faced with M and N which is which? Who knows, who cares, and why do this to yourself in the first place? Give your variables good, descriptive names and enjoy the time savings of being able to read your code when you are debugging it later.

The guts of usage are identical with array and vector:

 int test = arr[row * columns + column];

Will recover the element in 2D space at [row][column]. I shouldn't have to explain what any of those variables mean. Death to M and N.

Defining a function is:

void function (std::vector<int> & arr, size_t rows, size_t columns) 

or (yuck)

void function (int * arr, size_t rows, size_t columns) 

Note that rows and columns are of type size_t. size_t is unsigned (a negative array size is not something you want, so why allow it?) and it is guaranteed to be big enough to hold the largest possible array index you can use. In other words it is a much better fit than int. But why pass rows and columns everywhere? The smart thing to do at this point is make a wrapper around an the array and its control variables and then bolt on a few functions to make the thing easier to use.

template<class TYPE>
class Matrix
{
private:
    size_t rows, columns;
    std::vector<TYPE> matrix;
public: 
    // no default constructor. Matrix is BORN ready.

    Matrix(size_t numrows, size_t numcols):
        rows(numrows), columns(numcols), matrix(rows * columns)
    {
    }
// vector handles the Rule of Three for you. Don't need copy and move constructors
// a destructor or assignment and move operators

    // element accessor function
    TYPE & operator()(size_t row, size_t column)
    {
        // check bounds here
        return matrix[row * columns + column];
    }

    // constant element accessor function
    TYPE operator()(size_t row, size_t column) const
    {
        // check bounds here
        return matrix[row * columns + column];
    }

    // stupid little getter functions in case you need to know how big the matrix is
    size_t getRows() const
    {
        return rows;
    }
    size_t getColumns() const
    {
        return columns;
    }

    // and a handy-dandy stream output function
    friend std::ostream & operator<<(std::ostream &  out, const Matrix & in)
    {
        for (int i = 0; i < in.getRows(); i++)
        {
            for (int j = 0; j < in.getColumns(); j++)
            {
                out << in(i,j) << ' ';
            }
            out << '\n';
        }

        return out;
    }

};

Rough bash-out of what the array version would have to look like just to show the benefits of allowing vector to do its job. Not tested. May contain howlers. The point is a lot more code and a lot more room for error.

template<class TYPE>
class ArrayMatrix
{
private:
    size_t rows, columns;
    TYPE * matrix;
public:
    ArrayMatrix(size_t numrows, size_t numcols):
        rows(numrows), columns(numcols), matrix(new TYPE[rows * columns])
    {
    }

    // Array version needs the copy and move constructors to deal with that damn pointer
    ArrayMatrix(const ArrayMatrix & source):
        rows(source.rows), columns(source.columns), matrix(new TYPE[rows * columns])
    {
        for (size_t i = 0; i < rows * columns; i++)
        {
            matrix[i] = source.matrix[i];
        }
    }

    ArrayMatrix(ArrayMatrix && source):
        rows(source.rows), columns(source.columns), matrix(source.matrix)
    {
        source.rows = 0;
        source.columns = 0;
        source.matrix = nullptr;
    }

    // and it also needs a destructor
    ~ArrayMatrix()
    {
        delete[] matrix;
    }

    TYPE & operator()(size_t row, size_t column)
    {
        // check bounds here
        return matrix[row * columns + column];
    }

    TYPE operator()(size_t row, size_t column) const
    {
        // check bounds here
        return matrix[row * columns + column];
    }

    // and also needs assignment and move operator
    ArrayMatrix<TYPE> & operator=(const ArrayMatrix &source)
    {
        ArrayMatrix temp(source);
        swap(*this, temp); // copy and swap idiom. Read link below.
        // not following it exactly because operator=(ArrayMatrix source)
        // collides with operator=(ArrayMatrix && source) of move operator
        return *this;
    }

    ArrayMatrix<TYPE> & operator=(ArrayMatrix && source)
    {
        delete[] matrix;
        rows = source.rows;
        columns = source.columns;
        matrix = source.matrix;

        source.rows = 0;
        source.columns = 0;
        source.matrix = nullptr;

        return *this;
    }

    size_t getRows() const
    {
        return rows;
    }
    size_t getColumns() const
    {
        return columns;
    }
    friend std::ostream & operator<<(std::ostream &  out, const ArrayMatrix & in)
    {
        for (int i = 0; i < in.getRows(); i++)
        {
            for (int j = 0; j < in.getColumns(); j++)
            {
                out << in(i,j) << ' ';
            }
            out << std::endl;
        }

        return out;
    }

    //helper for swap.
    friend void swap(ArrayMatrix& first, ArrayMatrix& second)
    {
        std::swap(first.rows, second.rows);
        std::swap(first.columns, second.columns);
        std::swap(first.matrix, second.matrix);
    }

};

Creating one of these is

Matrix<int> arr(rows, columns);

Now passing the array around is

void func(Matrix & arr);

Using the array is

int test = arr(row, column);

All of the indexing math is hidden from sight.

Other references:

What is the copy-and-swap idiom?

What is The Rule of Three?

Community
  • 1
  • 1
user4581301
  • 29,019
  • 5
  • 26
  • 45
0

int &matrix[N][] - N has to be a compile-time constant, not just const and not at all a parameter. And reference to an array is declared like: int (&matrix)[size]

Try passing int **matrix, and you'll also need to change the way you create this array. Variable lenght arrays are not supported in C++, you'll need to allocate it's memory dynamically. Or rather, stick with std::vector, std::array if you knew the sizes at compile-time.

Community
  • 1
  • 1
LogicStuff
  • 18,687
  • 6
  • 49
  • 70