1

When I read some values from the user and I need to create an array of the specific size I do it somehow like this:

#include <iostream>
using namespace std;    
unsigned* numbers;
int main()
{
int a;
cin >> a;
numbers = new unsigned[a];
}

How can I do it with a 2d array(sized a*b read from the user)?

gen
  • 9,528
  • 14
  • 35
  • 64
  • 4
    But why? **whyyy?** (hint : std::vector) also, 2d arrays are evil. – Bartek Banachewicz Jan 11 '13 at 10:08
  • vector is also good for me but don't know how to make 2d of them `vector>` ??? – gen Jan 11 '13 at 10:09
  • 8
    @BartekBanachewicz , your comment is unconstructive for a beginner. gen , vectors of vectors are possible, but have associated performance penalties (which I won't bother to get into here) – Brennan Vincent Jan 11 '13 at 10:10
  • 2
    How so, @brennan? How is it unconstructive to suggest the only sensible solution? – Bartek Banachewicz Jan 11 '13 at 10:11
  • 8
    @BartekBanachewicz because it's not unreasonable to think that wherever he is learning C++ from thought it was worth teaching him the core language (what arrays and pointers are) before going into the standard library. A teaching strategy you may or may not agree with, but certainly a legitimate one. Simply telling him he's wrong with a "hint" that would seem very cryptic to someone who hasn't seen the C++ standard library is unhelpful. As is saying something is "evil" with no explanation. – Brennan Vincent Jan 11 '13 at 10:13
  • 4
    @Brennan Wrong, wrong, wrong. Who upvoted that comment? The standard library **is** the core language, and is far more fundamental than C-style arrays and pointers, *especially* in C++11 (but even before that). As teaching strategies go, yours is abysmal and counter-productive. Bartek’s advice is spot-on. – Konrad Rudolph Jan 11 '13 at 10:36
  • @BrennanVincent I'd disagree with the sentence stating "arrays and pointers are core of the C++ language". They are core of C subset, but they aren't required to write good C++ code at all. And oh, Konrad was a bit faster. – Bartek Banachewicz Jan 11 '13 at 10:37
  • @All of you The core language is not important for me at all, just please tell me how would you store an N*M table in C++? It could me std, core, not-core, anything but please tell me :). – gen Jan 11 '13 at 10:41
  • @gen There are already 8 answers below, including mine. – Bartek Banachewicz Jan 11 '13 at 10:42

8 Answers8

7

If a and b are the number of rows and number of columns, respectively, you may allocate the array like this:

new unsigned[a * b];

To access the element at row i and column j, do this:

numbers[i * b + j]

However, note that in reality you're almost certainly better off using std::vector for whatever you're trying to do, but you may not have learned about that yet :)

Brennan Vincent
  • 10,736
  • 9
  • 32
  • 54
  • I know a bit about vectors, are there any "tricky" things about them that I should know? – gen Jan 11 '13 at 10:12
  • You might be interested in the `reserve(n)` function on vectors. It makes sure the vector has enough memory allocated to hold whatever you are trying to store in it. Edit: It's not formally necessary. If you don't call it, the vector will handle reallocating new memory and copying over its contents to the new memory whenever it runs out of memory. So the behavior is the same with or without `reserve`. But you may want to call `reserve` to avoid all this recopying, for performance reasons. – Brennan Vincent Jan 11 '13 at 10:17
6

Anything that would look like a 2D-Array in code will not be a physical 2D array in memory, but either one plain block of memory or scattered, depending on how you allocate it.

  • You can torture yourself by doing dynamic allocation of N arrays in another dynamic array of N pointers, like nrussel's answer suggests.
  • You can make a vector of vectors instead, like billz and Arun C.B suggest - that would relieve you from managing the allocated memory yourself but still leave you with N+1 scattered allocations which is not very performant.
  • Brennan Vincent's answer suggests allocation of one dynamic array, containing a*b elements, which gives you one continuous block in memory. Combine that with the builtin dynamic memory management of std::vector he mentioned and be happy:

    std::vector<unsigned> matrix(a*b);

If you want the matrix to be convenient to access, wrap the whole thing into a class providing you access to the elements with 2D-coordinates. But please step away from managing the memory yourself. It just hurts you and anyone who has to maintain that code (and search for mem leaks).

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
  • This is the best answer so far, IMO. Gives (arguably) the correct answer, explains why it's (arguably) correct, and avoids condescension. – Brennan Vincent Jan 11 '13 at 10:21
  • tbh, I overlooked your note about std::vector, so it's just the answer you gave him, with a few additional explanations. – Arne Mertz Jan 11 '13 at 10:28
  • 2
    Well, using answers to make even better answers is the soul of stackexchange :) what's that Newton said about standing on the shoulders of giants... – Brennan Vincent Jan 11 '13 at 10:29
3

Could just use one vector to represent two dimension arrays:

   typedef std::vector<int>     d1_type;       // 1D
   typedef std::vector<d1_type> d2_type;       // 2D
   typedef std::vector<d2_type> d3_type;       // 3D


   int num = 5;
   d2_type value2d(num, d1_type(num));
   d3_type value3d(num, d2_type(num, d1_type(num)));

And you can access 2D/3D vector like array, for example:

 value2d[0][0] = 100;
 value3d[0][0][0] = 100;
billz
  • 44,644
  • 9
  • 83
  • 100
  • Be worth mentioning alternative constructors that would permit construction of the `vector` with specific dimensions. – hmjd Jan 11 '13 at 10:15
  • 1
    If your array isn't jagged (each row has the same number of entries) then you don't need to do this, you can just allocate everything within one vector, as I proposed in my answer. Then you can reserve the space you need all at once. – Brennan Vincent Jan 11 '13 at 10:15
  • sorry @hmjd, more specific? – billz Jan 11 '13 at 10:19
  • See [vector constructors](http://en.cppreference.com/w/cpp/container/vector/vector). There is a constructor that permits a `vector` to be constructed containing a specific number of elements. – hmjd Jan 11 '13 at 10:20
  • IF I want this 2d vector to be global, than at reading the values into the vector goes like this: `for(i) {numbers.push_back(); for(j) {cin >> a; numbers[i].push_back(a);}}` ?? – gen Jan 11 '13 at 10:23
  • This isnt 2D but a sparse matrix. – Dave Hillier Jan 11 '13 at 10:24
  • @gen use typedef and vector contstructor is much easier, see my pdated answer – billz Jan 11 '13 at 10:39
3

Updated recommendation:

It's worth noting that boost already did that. So I'll just stick to it, if it isn't meant to be encapsulated in a class.

Original answer:

In general, using raw arrays and allocations is inferior to using std::vector. Now, you can of course use vector<vector<T>>, but that's not only lenghty to type, but can also involve unnecessary copying.

Using ptr_vector or vector<unique_ptr<vector<int>> would solve the second problem, but complicate the matters even worse. So how to solve it?

It's simple: Don't use 2D arrays at all.

Imagine your 2D array in memory:

[X|X|X|X]
[Y|Y|Y|Y]
[Z|Z|Z|Z]

It's obvious enough that it could be put in one row:

[X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z]

Now we're back to old, familiar 1D array (doesn't matter if it's a vector or C-style array). In order to get to your element, you have to know the width of your initial array, and go to the array element corresponding to n'th row, then plainly add the number of columns:

int& access (unsigned x, unsigned y) { 
    return data[x * sizeY + y]; // Or (y * sizeX + x)
}

Since it's a stub method, you might have a problem in actual usage. Here's an example of a global implementation:

int& accessVectorAs2d (std::vector<int> & data, unsigned x, unsigned y, unsigned int sizeY); // inside stays the same
unsigned SizeX = 20, SizeY = 20;
std::vector<int> V (SizeX * SizeY, 0);

accessVectorAs2d(V, 1, 3, SizeX) = 5;

Reference is returned to allow using your 1D structure in a very similar way to normal 2D array:

// So you can do either reads
int X = access(1, 3);

// Or writes!
access(1,3) = 5;

Additional note: if you'll construct your own class, overloading operator() can give you even better results:

Data(1, 3) = 5;
int X = Data(1, 3);

Now how you're gonna implement that access (subclassing or encapsulating vector, or just using plain global function) doesn't really matter.

I'd strongly suggest you used std::vector here. It will ensure no memory will leak (forgotten delete), it will be easier to change the size (either by .push_back() or .reserve()) and is in general suggested practice; you're writing in C++, not in C, after all.

Behold! Dragons after this point!

Actually the class should of course be templated, with not only the type, but also number of dimensions (okay, that can be simplified) and size of (every-1) dimension (-1 to allow unlimited growth in one side). This would be probably done best by creating appropriate access functions at compile time, calculating

Sumi = 0i < n (Sizei)i * Di,

where n is number of dimensions, S is an array of dimension sizes, and D is an array of coordinates.

Community
  • 1
  • 1
Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
  • Good answer, I upvoted. Question: why return a reference from `access` ? – Brennan Vincent Jan 11 '13 at 10:24
  • I've added an explanation. – Bartek Banachewicz Jan 11 '13 at 10:29
  • You cannot overload `operator[]` with multiple arguments, you need to use `operator()` instead. – Konrad Rudolph Jan 11 '13 at 10:41
  • +1 for the effort, but two annotations: to use the array "look-and-feel", one could provide a inner class Row, making something like `Data[3][5]` possible. Just a nice-to-have, but not necessary to deal with the 2D. The other thing I'd like to mention: **DON'T** subclass vector. It's not designed to be a base class leading to splicing and similar kind of errors if you derive from it without using the derived class very carefully. There is no reason to choose subclassing over encapsulation ("too lazy to write the forwarding methods" doesn't count as a reason). – Arne Mertz Jan 11 '13 at 11:14
  • thanks @Arne, your input is very valuable. I also prefer encapsulating vector, because usually access to it should be made from a classanyway, and then a simple inline private method is just enough. – Bartek Banachewicz Jan 11 '13 at 14:12
2

Try to use vectors.

#include <iostream>
#include <vector>
using namespace std;

int main()
{
// An empty vector of vectors. The space 
// between the 2 '>' signs is necessary
vector<vector<int> > v2d;

// If you intend creating many vectors 
// of vectors, the following's tidier
  typedef vector<vector<int> > IntMatrix;
  IntMatrix m;

// Now we'll try to create a 3 by 5 "matrix".
// First, create a vector with 5 elements
   vector<int> v2(5, 99); 

// Now create a vector of 3 elements. 
 // Each element is a copy of v2
 vector<vector<int> > v2d2(3,v2);

// Print out the elements
 for(int i=0;i<v2d2.size(); i++) {
  for (int j=0;j<v2d2[i].size(); j++)
    cout << v2d2[i][j] << " "; 
    cout << endl;
   }
}
1

You can allocate the memory by using c++ new[] the following way :

int** array = new int*[x];
for(int i = 0; i < x; i++)
    aryay[i] = new int[y];

The simple concept of this is that its and array of pointers which are pointing to arrays. It must be deleted too to avoid the memory leaks.

Aadil Imran
  • 105
  • 1
  • 5
1

Although the "vector of vectors" solution can be satisfying, they are not a 2D structure: each "row" will be allocated independently of another, and length can be different row by raw (and hence the 2D constrain must be maintained manually: inserting an item requires all rows to be enlarged so that elemets that are one above the other maintain their relative position.

In case you need a proper dynamic 2D structure, you can wrap a vector (simple mono-dimensional) in a class (let's call it "table") and provide that class with the operation requird to properly maintain it as an externally represented 2D. In particular:

  • Define a Colums() const and Rows() const functions retuning theactual size of the table.
  • Define a int& operator()(unsigned row, unsigned col) { return vect[row*Colums()+col]; } and it s const counterpart
  • Define a struct coord { unsigned r, unsigned c } and a convenient table::operator[](const coord&)
  • Define insert and remove methods to insert a row (just insert Colums() consecutive elements at r*Columns()) and a column (insert one element every Columns()+1 starting from c)
  • Define proper iterator classes that walk along colums (++ advance by 1 element) and rows (++ advance by Colums() elements) (Beware to consitently define an end() value for them)

The key point of all this stuff is the understanding of the n = r*C+cformula at the second point. Everything else is just an almost immediate consequence.

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
0
unsigned **numbers;
numbers = new unsigned*[a];
for (int i=0; i<a; i++){
    numbers[i] = new unsigned[b];
}

It doesn't behave exactly like a 2d array, in that it's not contiguous in memory, but it will do. You can also use the notation numbers[i][j] to access elements.

Remember to delete[] each element of numbers at the end before delete[]-ing numbers itself

Using std::vector is probably a preferable solution, as detailed in other posts

nrussell
  • 358
  • 1
  • 7