I have a project and I have to define an array of arrays of different dimensions (like a triangle) because I am not allowed to use std::vector or other container class. For this, I am using an array of pointers. Normally, I would do this:
int* triangle[n];
for(int i = 0; i < n; i++) {
triangle[i] = new int[i + 1];
for(int j = 0; j <= i; j++) cin >> triangle[i][j];
}
But I must not use dynamic memory! I thought that doing
int* triangle[n];
for(int i = 0; i < n; i++) {
int row[i + 1];
triangle[i] = row;
for(int j = 0; j <= i; j++) cin >> triangle[i][j];
}
would do the trick. But it didn't. Instead, when I iterate over the arrays and print the contents, I get garbage. So, how can I replace a dynamic allocation with a static one?
So, if you want "array of arrays" simplified, your choice is array linearization. It means that instead of 7 arrays of 10 items each you should declare single array of 70 elements. Then, just change nested indexes with a function that calculates resulting shift in linearized array, and viola!
Here you can find one of such examples: How to map the indexes of a matrix to a 1-dimensional array (C++)?
In case you don't know in advance how long is your array, it may be a tough choice to determine preliminary reservation size (for example, STL containers like vector etc. do the same trick: they allocate a chunk of memory then grow the container until free capacity left, then re-allocate bigger chunk moving the data from old to new buffer, and again, and again, and again...)
As #BeyelerStudios pointed out in the comments, at the end of the for loop, the memory allocated for the row array gets freed. When I am trying to print out the contents, I am dereferencing pointers to this freed up memory, so I am getting garbage. Thank you!
Related
Currently, C++ only allows declaring arrays with length as integers. Is there any way to get around it? I am trying to create a program that generates an array that can potentially reach thousands or even millions in length depending on input, but array declaration limiting to integer-only length is holding me back.
Comment Basically, supposing that I want to create a 2d array with 5 rows and 500,000 columns, I get a segmentation fault.
The prototype for std::array is
template<class T, std::size_t N> struct array;
According to https://en.cppreference.com/w/cpp/types/size_t,
std::size_t can store the maximum size of a theoretically possible
object of any type (including array). A type whose size cannot be
represented by std::size_t is ill-formed (since C++14)
(emphasis mine)
So C++, by definition, cannot express the concept of an object whose size is larger than a std::size_t.
Fortunately, on most platforms std::size_t is going to be 32 bits at a minimum, meaning it can reach not only millions but billions. And if you're on a platform where it's smaller than that then presumably your hardware isn't physically capable of storing larger objects anyway.
UPDATE: In the comments you add
Sorry, I ought to have been more specific. Basically, supposing that I
want to create a 2d array with 5 rows and 500,000 columns, I get a
segmentation fault.
If the problem here were a limitation of the language, you would get a compiler error, not a runtime error.
Since you're getting a runtime error, the problem is with your platform, not the language. In this particular case, the "problem" is that it doesn't give you enough stack space to support multi-megabyte objects in a stack frame. (This is a pretty sensible limitation, if you ask me.) Instead, you'll want to allocate your data on the heap.
I could hand you some code that would just make your problem go away for now, but what you really need to do is read about the stack and heap and understand what they are and how to use them.
#include <iostream>
int main() {
int n = 0;
std::cin >> n;
int *arr = new int[n]; // dynamic declaration of variable length array
for(int i = 0; i < n; ++i)
std::cin >> arr[i]; // read array elements
return 0;
}
EDIT: For 2d array
#include <iostream>
int main() {
int **arr;
int r, c;
std::cin >> r >> c;
// Create an array of row heads
arr = new int *[r];
// Create an 2d array
for (int i = 0; i < r; ++i) arr[i] = new int[c];
// read input in 2d array
for (int i = 0; i < r; ++i)
for (int j = 0; j < c; ++j) std::cin >> arr[i][j];
// print 2d array
for (int i = 0; i < r; ++i)
for (int j = 0; j < c; ++j) std::cout << arr[i][j] << ' ';
return 0;
}
std::vectoris a better choice. You only need push_back or emplace_backto add element into it.
More details:
http://www.cplusplus.com/reference/vector/vector/
I am trying to create an array, which doubles every time it is completely filled.
#include <iostream>
using namespace std;
int* array_doubler(int* array, int size){
int *new_array = new int[size*2];
for(int i = 0; i < size; i++){
new_array[i] = array[i];
}
delete[] array;
return new_array;
}
int main()
{
int N = 10; // Size of array to be created
int *array = new int[0];
for(int i = 0; i < N; i++)
{
if(array[i] == '\0')
array = array_doubler(array, i);
array[i] = i*2;
}
//Printing array elemensts
for(int i = 0; i < N; i++)
cout << array[i] << '\t';
cout << '\n';
return 0;
}
Problem is when I create dynamic memory with new, all the spots have the null character \0 value in them (not just the last spot). i.e. If i write:
int* p = new int[5];
then all the 5 blocks in memory p[0],p[1],p[2],p[3],p[4],p[5] have \0 in them, not just the p[5]. So the if(array[i] == '\0') in my main() calls array_doubler for every single iteration of for loop. I want it to fill the available spots in the array first and when it reaches the last element, then call array_doubler.
Problem is when I create dynamic memory with new, all the spots have the null character \0 value in them (not just the last spot).
Actually they have undefined values in them. 0 is a valid value for them to have, but tomorrow the compiler might suddenly decide that they should all have 1 instead of 0.
If you want to detect the end of an array, then you have to remember how big the array is. C++ doesn't do it for you. Actually, it does do it for you if you use std::vector, but I suppose that's not the point of this exercise.
I'm not sure why you'd want to do this, as std::vector offer this kind of feature, and are more idiomatic of c++ (see isocpp faq on why C-style array are evil).
One of the issue of C-style array is the fact that they donĀ“t know their own size, and that they don't have default value, thus stay uninitialized.
If for some reason you need to not use std::vector, the next best solution would be to wrap the array with it's size in a structure or a class (which is kinda what std::vector is doing), or to initialize your array using std::memset (which is the C function you would use if you were in C).
Do keep in mind that this is not considered as good practices, and that the STL offer plenty of solution when you need containers.
I need to create a square matrix of a given size. I know how to create a dynamic one-dimensional array of a given size. Doesn't the same work for two dimensinal arrays like the lines below?
cin>>size;
int* a[][]=new int[size][size]
int* a[][]=new int[size][size]
No, this doesn't work.
main.cpp:4: error: only the first dimension of an allocated array may have dynamic size
new int[size][size];
^~~~
If the size of the rows were fixed then you could do:
// allocate an array with `size` rows and 10 columns
int (*array)[10] = new int[size][10];
In C++ you can't have raw arrays with two dimensions where both dimensions are dynamic. This is because raw array indexing works in terms of pointers; for example, in order to access the second row a pointer to the first needs to be incremented by the size of the row. But when the size of a row is dynamic the array doesn't know that size and so C++ doesn't know how to figure out how to do the pointer increment.
If you want an array with multiple dynamic dimensions, then you need to either structure the array allocations such that C++'s default array indexing logic can handle it (such as the top answers to this duplicate question), or you need to implement the logic for figuring out the appropriate pointer increments yourself.
For an array where each row has the same size I would recommend against using multiple allocations such as those answers suggest, or using a vector of vectors. Using a vector of vectors addresses the difficulty and dangerousness of doing the allocations by hand, but it still uses more memory than necessary and doesn't allow faster memory access patterns.
A different approach, flattening the multi-dimensional array, can make for code as easy to read and write as any other approach, doesn't use extra memory, and can perform much, much better.
A flattened array means you use just a single dimentional array that has the same number of elements as your desired 2D array, and you perform arithmetic for converting between the multi-dimensional indices and the corresponding single dimensional index. With new it looks like:
int *arr = new int[row_count * column_count];
Row i, column j in the 2d array corresponds to arr[column_count*i + j]. arr[n] corresponds to the element at row n/column_count and column n% column_count. For example, in an array with 10 columns, row 0 column 0 corresponds to arr[0]; row 0, column 1 correponds to arr[1]; row 1 column 0 correponds to arr[10]; row 1, column 1 corresponds to arr[11].
You should avoid doing manual memory management using raw new and delete, such as in the case of int *arr = new int[size];. Instead resource management should be wrapped up inside a RAII class. One example of a RAII class for managing dynamically allocated memory is std::vector.
std::vector<int> arr(row_count * column_count);
arr[column_count*i + j]
You can further wrap the logic for computing indices up in another class:
#include <vector>
class Array2d {
std::vector<int> arr;
int columns;
public:
Array2d(int rows, int columns)
: arr(rows * columns)
, columns(columns)
{}
struct Array2dindex { int row; int column; };
int &operator[] (Array2dindex i) {
return arr[columns*i.row + i.column];
}
};
#include <iostream>
int main() {
int size;
std::cin >> size;
Array2d arr(size, size);
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
arr[{i, j}] = 100;
}
}
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
std::cout << arr[{i, j}] << ' ';
}
std::cout << '\n';
}
}
If you're using C++11 you can also use std::array.
const int iRows = 3, iCols = 3; // number of rows and columns
std::array<std::array<int, iCols>, iRows> matrix;
// fill with 1,2,3 4,5,6 7,8,9
for(int i=0;i<iRows;++i)
for(int j=0;j<iCols;++j)
matrix[i][j] = i * iCols + j + 1;
This class also allows for bounds checking by using the function
std::array::at
which (just like operator[]) returns a const reference if the array-object is const-qualified or a reference if it is not. Please note that
std::array
is not a variable-sized array-type, like
std::vector
You can use std::vector:
std::vector<std::vector<int*>> a(size, std::vector<int*>(size));
This will create a dynamically allocated 2D array of int* with width and height equal to size.
Or the same with new:
int*** a = new int**[size];
for (size_t i = 0; i < size; ++i)
a[i] = new int*[size];
...
for (size_t i = 0; i < size; ++i)
delete a[i];
delete a;
Note that there's no new[][] operator in C++, you just have to call new[] twice.
However, if you want to do it with new and delete instead of std::vector, you should use smart pointers instead of raw pointers, for example:
std::unique_ptr<std::unique_ptr<int*>[]> a(new std::unique_ptr<int*>[size]);
for (size_t i = 0; i < size; ++i)
a[i].reset(new int*[size]);
...
// No need to call `delete`, std::unique_ptr does it automatically.
So the lecture example has the following code
int **a;
a = new int*[10];
for (int i = 0; i < 10; ++i){
a[i] = new int[5];
for (int j = 0; j < 5; ++j){
a[i][j] = i*10+5;
}
}
And I have a few questions about the above code (and multidimensional arrays on the heap in general):
Do we have to initialize every element in the array as shown? Or is it suffice to cut the code after the a[i] = new int [5]?
I know with arrays allocated on the stack, it is illegal to use a variable as the stack size as
cin >> n;
int a[n];
but is it legal for heap-allocated arrays? As in, a = new int[n]?
Why is it a double pointer pointing to this array? Usually for 1D arrays, we used a single pointer as int *a = new int[10]? If I wanted the value of the array element itself, do I deference twice as **a? Or do I still do *a?
So say I have a 2D array of objects of some Class. How would I access the member field var of the ith, jth element? What if I want to invoke the member function of the object in the ith, jth element?
You don't have to initialize every element in the array. If you don't, the contents of the array will be undefined. Alternatively, you can zero-initialize with new int[5]().
a = new int[n] works on the heap because there's actually a run-time call to mark new memory for use as the data a is pointing to. It can't work on the stack because the compiler needs to know how big the stack frame is for any particular function call, so the value must be computable at compile time.
You need a double pointer because a is a pointer to a pointer to an integer. It points to an array of arrays, each of which needs to be dereferenced when accessing the value. You would typically use a[i][j] to access a specific element, which effectively double-deferences.
If the number of columns in the matrix is known at compile time, you can just allocate a matrix:
int nrow = 10; // number of rows assigned at run time
int (*a)[5]; // pointer to array of 5 ints
a = new int[nrow][5]; // allocate matrix
for (int i = 0; i < nrow; ++i) // initialize matrix
for (int j = 0; j < 5; ++j)
a[i][j] = i*nrow+j;
// ...
delete[] a;
I have allocated an array as follows.
#include <iostream>
int main() {
const int first_dim = 3;
const int second_dim = 2;
// Allocate array and populate with dummy data
int** myArray = new int*[first_dim];
for (int i = 0; i < first_dim; i++) {
myArray[i] = new int[second_dim];
for (int j = 0; j < second_dim; j++) {
myArray[i][j] = i*second_dim + j;
std::cout << "[i = " << i << ", j = " << j << "] Value: " << myArray[i][j] << "\n";
}
}
// De-allocate array
for (int i = 0; i < first_dim; i++)
delete[] myArray[i];
delete[] myArray;
}
Let's say I want to add a 4th element to the first dimension, i.e. myArray[3]. Is this possible?
I've heard that Vectors are so much more efficient for this purpose, but I hardly know what they are and I've never used them before.
Yes, but in a very painful way. What you have to do is allocate new memory which now has your new desired dimensions, in this case 4 and 2, then copy all the contents of your matrix to your new matrix, and then free the memory of the previous matrix... that's painful. Now let's see how the same is done with vectors:
#include <vector>
using std::vector;
int main()
{
vector< vector <int> > matrix;
matrix.resize(3);
for(int i = 0; i < 3; ++i)
matrix[i].resize(2);
matrix[0][1] = 4;
//...
//now you want to make the first dimension 4? Piece of cake
matrix.resize(4);
matrix[3].resize(2);
}
HTH
edit:
some comments on your original code:
In C++ ALL_CAP_NAMES usually refer to macros (something you #define). Avoid using them in other contexts
why do you declare FIRSTDIM and SECONDDIM static? That is absolutely unnecessary. If a local variable is static it means informally that it will be the same variable next time you call the function with kept value. Since you technically can't call main a second sime this is useless. Even if you could do that it would still be useless.
you should wrire delete [] array[i]; and delete [] array; so the compiler knows that the int* and int** you're trying to delete actually point to an array, not just an int or int* respectively.
Let's say I want to add a 4th element to the first dimension, i.e. myArray[3]. Is this possible?
Yes, but it's a pain in the neck. It basically boils down to allocating a new array, just as your existing code does (hint: put it in the function and make the sizes arguments to that function) and copying compatible elements over.
Edit: One of the things that std::vector does for you is properly de-allocating you memory. In the code you have, failure to allocate one of the arrays along the 2nd dimension will result in a memory leak. A more robust solution would initialize pointers to 0 before performing any allocation. An exception block could then catch the exception and free whatever was partially allocated.
Because this code becomes complex quickly, people resort to allocating a single buffer and addressing using a stride or using a 1D array of 1D arrrays (i.e. std::vector of std::vectors).