1

I'm working on a project that allows a user to row reduce matrices of any size, the size determined by user input. I've done this with a vector of vectors, but I can't figure out how to do it with arrays. I'm a beginner for coding, so I've only ever dealt with 1 dimensional arrays.

How can I initialize a 2 dimensional array with size from user input?

bacoban30
  • 11
  • 1
  • 3
    You can't. Arrays must have a size known at compile time. Keep using the vector. To make it more efficent you can wrap a 1d vector in a matrix class and provide you own access to fake that the 1d vector has 2d. example: https://stackoverflow.com/a/2076668/4342498 – NathanOliver Aug 20 '22 at 21:00
  • Uh, don't like the answer linked by Nathan for abusing the function call operator as index operator – and fully disagree with the link provided there about the interface... Would rather appreciate the `[x,y]` syntax C# comes with, unfortunately that we cannot get with C++ – and `[x][y]` is next best way in my eyes (or even better?), though that requires a bit of extra work... – Aconcagua Aug 20 '22 at 22:12
  • @Aconcagua Note multidimensional subscript operator is coming in C++23: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2128r6.pdf – Ranoiaetep Aug 20 '22 at 22:38
  • @Ranoiaetep Uh... Tuple as index argument? Interesting idea – struggling in between the disadvantages of multiple subscripts and the need of additional braces... Well, really welcoming the proposition, though. Is it approved already? – Aconcagua Aug 21 '22 at 09:13
  • @Aconcagua I think you’ve read the proposal wrong. Tuple as index is a workaround that is available now. The proposal meant to allow syntax like `arr3d[x, y, z]`. And yes, it’s been approved already. – Ranoiaetep Aug 21 '22 at 12:38
  • @Ranoiaetep No, I didn't – was fully aware of all of these being workarounds – though never thought of using a tuple as parameter for this purpose – with the interest for only arising as for now, C++23 is still a peek into the future. – Aconcagua Aug 21 '22 at 12:58

2 Answers2

1

You will need to use some form of heap-storage for a user-specified "2D-array" (e.g. a vector<vector<T>>). This is necessary since arrays have their sizes fixed at compile-time.

Because the value won't be known until runtime, there really isn't an alternative available. That said, there are two different approaches you can use with heap memory:

  1. Nested containers (e.g. vector<vector<...>>), and
  2. One container using arithmetic to produce a 2D "projection"

1. Nested containers

A container of a container like a std::vector<std::vector<T>> is likely the easiest way. This is the recommended approach over manually-managed heap-allocated pointers (e.g. don't use new T*[N] followed by a bunch of new T[M] pointers. See 'Why should C++ programmers minimize use of 'new'?' for more details).

This can be done easily:

auto rows    = std::size_t{};
auto columns = std::size_t{};

// Get the input (ignoring prompts for the sake of brevity)
std::cin >> rows;
std::cin >> columns;

// using 'T' as a placeholder for the type
auto array_2d = std::vector<std::vector<T>>{}; 
array_2d.reserve(rows);

// Create 'row' number of vector objects
for (auto i = 0u; i < rows; ++i) {
    array_2d.push_back();     // create a new vector
    array_2d.resize(columns); // resize the vector to the number of columns
}

Note that this does not create a true "2d array" -- but rather it creates a container that holds row number of containers, each which is a container holding column T objects.

2. A single container, with a projection

The second way is to use a single container, such as a std::vector<T>, but to write a wrapper that projects a 2D array over it. For example, you could have a get(row, column) function that will access the element in the single contiguous vector and return it. This creates only 1 contiguous chunk of objects for the vector, but it's also (slightly) more complicated.

class Array2D {
public:

    Array2D(std::size_t rows, std::size_t columns)
        : m_data{},
          m_rows{rows},
          m_columns{columns}
    {
        m_data.resize(rows * columns);
    }

    auto get(std::size_t row, std::size_t column) -> T& {
        // you could also do checking here
        return m_data[row * m_columns + column];
    }
    auto get(std::size_t row, std::size_t column) const -> const T&; // can do the same for a const-qualified one...

    // Note: if you are using C++23, you can also have operator[] with
    //       more than one argument

    // ...

private:

    std::vector<T> m_data;
    std::size_t m_rows;
    std::size_t m_columns;
};

This would then be used like:

auto rows    = std::size_t{};
auto columns = std::size_t{};

// Get the input (ignoring prompts for the sake of brevity)
std::cin >> rows;
std::cin >> columns;

auto array = Array2D{rows, columns};

// Get a reference to a value
auto& v = array.get(0,5);

// Set a value
array.get(0,5) = ...

if you want to keep an array[row][column] syntax, you could also implement operator[] to return a proxy object so that you could make the syntax behave more like a 2D array:

class Array2DProxy {
public:
    explicit Array2DProxy(T* row) : m_row{row}{}

    auto operator[](std::size_t column) -> T& { 
        return m_row[column];
    }

private:

    T* m_row;
};

class Array2D {
    ...
    auto operator[](std::size_t row) -> Array2DProxy {
        // Return a proxy object using a pointer to the start of the row
        return Array2DProxy{&m_data[row * m_columns]};
    } 
    ...
}
Human-Compiler
  • 11,022
  • 1
  • 32
  • 59
  • I'd yet make the proxy a nested class of `Array2D` and make its pointer constructor private as well as the parent class a friend to enable it constructing one... – Aconcagua Aug 21 '22 at 09:30
-2
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int row; //this is for number of rows
    cin>>row; 

    // creating array of pointer of size row
    int** arr = new int*[row];

    for (int i = 0; i < row; i++) {
        int col; 
        /*
        col is the column size for each row and
        for each row column size can vary if user gives 
        different value of column for each row
        */
        cin>>col; 
    
        arr[i] = new int[col];

        for (int j = 0; j < col; j++) {
            int value;
            cin>>value;

            arr[i][j] = value;

        
        }
    }

    return 0;
}
  • can you please name those compilers on which this code doesn't run? – Amit kumar Sahni Aug 20 '22 at 22:44
  • Oops, my bad. I have no idea why I misread your answer. You are of course not using VLAs. So the only issue is the non-standard header. Sorry for that. (I am not the downvoter btw.) – user17732522 Aug 20 '22 at 22:49
  • No problem :) For header, we can even use #include instead of that header. #include contains all the important library so that we don't need to import it explicitly – Amit kumar Sahni Aug 21 '22 at 04:03
  • @AmitkumarSahni `` is a *GCC exclusive* header that does not exist at other compilers! Don't [*ever* include it](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h) – not even if you explicitly target GCC (unless *perhaps* exactly one specific version), it might get abolished there with some compiler version (though admittedly not too likely...). – Aconcagua Aug 21 '22 at 09:17
  • This solution doesn't have any specific advantage over a vector of vectors, which under the hoods does the same, but relieves you from all the necessary manual memory management – in your case you entirely missed to `delete[]` those arrays again! – Aconcagua Aug 21 '22 at 09:20
  • About [`using namespace std`](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice)... – Aconcagua Aug 21 '22 at 09:22