1

How would I declare a two dimensional array of size n by n which the user gives?
For example, why would this work:

int n;
cin >> n;
int *array = new int[n];
//no errors

but this would not:

int n;
cin >> n;
int *array = new int[n][n];
// these errors:
//error: array size in new-expression must be constant
//error: the value of 'n' is not usable in a constant expression
//note: 'int n' is not const

Is it possible to have a two dimensional array of row and column size n or would you have to use a vector?
Thanks!

David
  • 21
  • 5
  • 1
    If you say, … does not work, please include the error message always – caylee Oct 28 '17 at 07:54
  • I'm wondering which is more important in principle, the amount of memory being allocated dynamically or the dimensionality... – BroVic Oct 28 '17 at 17:15

5 Answers5

2

Do it with a vector, like this:

int n;
cin >> n;
auto matrix = std::vector<std::vector<int>>(n, std::vector<int>(n));

https://wandbox.org/permlink/zsOiUTvCbxAGmK6d


There is also a std::valarray template which is similar functionality as std::vector, but has interesting extra functionality, which can be useful in case of matrix.

https://wandbox.org/permlink/9Vq141QpEWcWoUxM

Marek R
  • 32,568
  • 6
  • 55
  • 140
  • I was gonna say use `unique_ptr` or `shared_ptr` but there are about a billion ways to skin this cat. – jcarpenter2 Oct 28 '17 at 07:58
  • Why would you do std:: when you can do "using namespace std;" – David Oct 28 '17 at 08:07
  • habit from coding standard in project I'm working on. – Marek R Oct 28 '17 at 08:09
  • don't do it with `new` `delete` since soon or later it will end with code hard to maintain and a memory leak. – Marek R Oct 28 '17 at 08:12
  • @David: Because you cannot use it in header files anyway, so why not just be consistent everywhere in your code? I also think the extra `std::` make the code more readable, because the standard-library components stand out visually. – Christian Hackl Oct 28 '17 at 08:22
  • @David Read here: https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice – user0042 Oct 28 '17 at 08:23
  • @ChristianHackl Oh I thought you could use it in header files if you just put using namespace std; I am still relatively new to programming and c++ – David Oct 28 '17 at 08:29
  • @David: You technically can, but it's considered bad practice, as it may break other parts of the code in an unexpected way. See https://stackoverflow.com/a/1453605/3313064 for an excellent answer on this topic. – Christian Hackl Oct 28 '17 at 08:44
  • It should be noted this creates a vector of vectors and not a two-dimensional arrays. These have different performance characteristics, different amounts of memory requirements, and different memory layouts. E.g., a vector of vectors requires every vector within it to be allocated and initialized. It is something of a waste to initialize memory management data for multiple vectors when they will all be identical lengths. – Eric Postpischil Oct 28 '17 at 15:30
  • Yes, but this is answer for newbie, so writing about better solutions is pointless. – Marek R Oct 28 '17 at 17:39
  • 1
    @MarekR: You do not have to write about newer solutions to include a note that this is not really a two-dimensional array and has issues that the student ought to learn about as they gain experience. Furthermore, Stack Overflow is a durable repository of knowledge, not just a homework-help site. – Eric Postpischil Oct 28 '17 at 18:33
  • Oops, “better solutions,” not “newer solutions.” – Eric Postpischil Oct 28 '17 at 22:28
1

To create a multidimensional array, you would have to write this:

int n;
cin >> n;
int **array = new int*[n];
for (int i = 0; i < n; i++) {
    array[i] = new int[n];
}

And don't forget to delete it:

for (int i = 0; i < n; i++) {
    delete[] array[i];
}
delete[] array;

But current c++ best practice is to never allocate or deallocate memory in application code, meaning you should use a std::vector or some other container class.

jcarpenter2
  • 5,312
  • 4
  • 22
  • 49
  • Would this create an array of n dimensions? Sorry, I wasn't specific enough, I meant I wanted to create a 2D array or size n by n. – David Oct 28 '17 at 08:17
  • No, this array has only 2 dimensions. The code just allocates memory for each first-level entry. – caylee Oct 28 '17 at 08:18
  • 1
    It should be noted this creates an array of pointers to arrays of `int`. While this can be addressed syntactically like a two-dimensional array, it has different performance characteristics and different memory layouts. E.g., every reference to the array requires an extra memory reference to get a pointer, where a reference to an actual two-dimensional array would use an in-register calculation (which the compiler is often able to optimize in various ways). These differences might not matter much to a novice learning to program, but they should be aware of the issues as their experience grows. – Eric Postpischil Oct 28 '17 at 15:33
1

This answer differs in two points from most of the other answers:

  • no raw pointer or memory allocation
  • use a single contiguous (linear) block of memory

A common approach is using a single linear block of memory and storing the elements row after row or column after column (Row- or column-major order). Let us generalize a bit and say you want a 2D quantity of type T with the extents N0 and N1. Then you can obtain the required memory by

std::vector<T> my_memory(N0*N1, T{});

Using a vector instead of a raw pointer has several advantages. For example, you can not forget to free the memory because the deconstructor cares about that automatically (RAII idiom). Another advantage is that the constructor called above fills the vector with copies of default-constructed T (instead of leaving the memory uninitialized).

Next, you can access the linear memory by using vector's member operator[] (or the member function at if you want bound checks). You probably want to use two indices n0 and n1 in the intervals [0, N0) and [0, N1), but the memory is linear and requires a single index. Hence you can introduce a helper function index in order to access your 2D quantity as, say,

my_memory[index(n0, n1)] = T(42);

To imagine how this could work consider a 3-by-2 matrix. In the given linear memory block you could arrange the elements like this:

         0           1           2           3           4           5
index(0, 0) index(0, 1) index(1, 0) index(1, 1) index(2, 0) index(2, 1)

In which case you would write

int index(int n0, int n1) {
  return n0 * N1 + n1;// (note that `n0` is multiplied by `N1`).
}

In general for d dimensions you would have (pseudo code)

int index(int(&n)[d]) {
  return n[0] * (N[1]*N[2]*...*N[d-1])
    + n[1] * (N[2]*N[3]*...*N[d-1])
    + ...
    + n[d-2] * (N[d-1])
    + n[d-1] * (1);
}

where the expressions in brackets are also called pitches.

If you put all that logic in a class, is becomes very simple to use. Here is an example for inspiration (online demo with N = 4):

#include <cassert>

#include <iostream>
#include <vector>

template<class T>
class Quantity2d {
 private:
  std::size_t Ns_[2];
  std::vector<T> data_;
 public:
  Quantity2d(int N0, int N1)
  : Ns_{std::size_t(N0), std::size_t(N1)}
  , data_()
  {
    assert(N0 > 0);
    assert(N1 > 0);
    data_.resize(size(), T{});
  }

  constexpr size_t size() const { return Ns_[0] * Ns_[1]; }

  constexpr int N0() const { return int(Ns_[0]); }
  constexpr int N1() const { return int(Ns_[1]); }

  constexpr size_t pitch0() const { return Ns_[1]; }
  constexpr size_t pitch1() const { return std::size_t(1); }

  constexpr size_t ind(int n0, int n1) const {
    assert(n0 >= 0 && n0 < N0());
    assert(n1 >= 0 && n1 < N1());
    return std::size_t(n0) * pitch0() + std::size_t(n1) * pitch1();
  }

  T& operator()(int n1, int n2) {
    return data_[ind(n1, n2)];
  }

  constexpr const T& operator()(int n1, int n2) const {
    return data_[ind(n1, n2)];
  }
};

template<class T>
std::ostream& operator<<(std::ostream& os, const Quantity2d<T>& q) {
  int N0 = q.N0();
  int N1 = q.N1();
  for(int i=0; i<N0; ++i) {
    for(int j=0; j<N1; ++j) {
      os << i << "\t" << j << "\t" << q(i, j) << "\n";
    }
  }
  return os;
}

int main() {
  int N = 0;
  std::cout << "please enter size per dimension "
    << "(WARNING: matrix will be printed on screen): "
    << std::flush;
  std::cin >> N;

  if(N >= 1) {
    std::cout << "I understood. So let's do " << N << "x" << N << std::endl;
  }
  else {
    std::cerr << "You failed. Next time try with N >= 1." << std::endl;
    return 1;
  }

  Quantity2d<double> matrix(N, N);

  for(int i=0; i<matrix.N0(); ++i) {
    for(int j=0; j<matrix.N1(); ++j) {
      matrix(i, j) = double(i) * double(j);
    }
  }

  std::cout << matrix << std::flush;

  return 0;
}
Julius
  • 1,816
  • 10
  • 14
0

It is possible to create a two dimensional array without using vectors like below. This will work for all the types.

template <typename T> 
T **Alloc2D( int nRows, int nCols)
{
      T **array2D;    
      array2D = new T*[nRows];
      for( int i = 0 ; i < nRows ; i++ )
          array2D[i] = new T [nCols];

      return array2D;
}

template <typename T>
void Free2D(T** dArray)
{
      delete [] *dArray;
      delete [] dArray;
}

But, the problem is memory management as it is a raw approach.

Instead, we can use vectors which is manageable like

typedef vector<vector<int>> ARRAY2D;
  • 1
    It should be noted this creates an array of pointers to arrays of `int`. While this can be addressed syntactically like a two-dimensional array, it has different performance characteristics and different memory layouts. E.g., every reference to the array requires an extra memory reference to get a pointer, where a reference to an actual two-dimensional array would use an in-register calculation (which the compiler is often able to optimize in various ways). These differences might not matter much to a novice learning to program, but they should be aware of the issues as their experience grows. – Eric Postpischil Oct 28 '17 at 15:34
-1

It is possible:

int **marray = new int[n][n];
caylee
  • 921
  • 6
  • 12
  • It is not wrong. `n` just need to be constant. A multidimensional array (as asked for) is possible. For dynamic size, e.g. `= calloc(n*n, sizeof(int))` would work – caylee Oct 28 '17 at 08:10
  • But n is not constant. The original question specifically talks about an n that was given by the user. – Aziuth Oct 28 '17 at 08:14
  • Ok, I see your point. See @jcarpenter2's answer, it is better indeed. – caylee Oct 28 '17 at 08:16