Instantiating a 3D array in C++ using parameters - c++

Sorry if this is a noob question, but I'm currently learning C++. I have a function that takes in several parameters - I would like to use those parameters when creating a 3D int array.
void* testFunction(int countX, int countY, int countZ)
{
const int NX = countX;
const int NY = countY;
const int NZ = countZ;
int* data_out = new int*[NX][NY][NZ];
// The above line throws the error on "NY" - expression must
// have a constant value
}
From various posts I've learned that you must allocate the array first but I guess I'm doing that wrong? How do you properly initialize a multidimensional array. Also, why does the initialization require a pointer?

To explain the error: C++ requires a name of a type in its new operator. A name of a type cannot have runtime dimensions, because all types in C++ are static (determined at compilation time).
For example, this allocates 3 elements of type int[4][5]:
new int[3][4][5];
Another example: this allocates NX elements of type int[4][5]:
new int[NX][4][5];
An incorrect example: this would allocate NX elements of type int[NY][NZ], if C++ had support for "dynamic" types:
new int[NX][NY][NZ];
To allocate a 3-dimensional array, or something that looks like it, you can use std::vector:
std::vector<std::vector<std::vector<int>>> my_3D_array;
... // initialization goes here
my_3D_array[2][2][2] = 222; // whatever you want to do with it
To make the syntax less verbose, and streamline the initialization, use typedef (or here using, which is the same):
using int_1D = std::vector<int>; // this is a 1-dimensional array type
using int_2D = std::vector<int_1D>; // this is a 2-dimensional array type
using int_3D = std::vector<int_2D>; // this is a 3-dimensional array type
int_3D data(NX, int_2D(NY, int_1D(NZ))); // allocate a 3-D array, elements initialized to 0
data[2][2][2] = 222;
If you want to return this array from your function, you should declare it; you cannot just return a void pointer to the data variable. Here is a syntax of a declaration:
using int_1D = std::vector<int>;
using int_2D = std::vector<int_1D>;
using int_3D = std::vector<int_2D>;
int_3D testFunction(int countX, int countY, int countZ)
{
int_3D data(...);
...
return data;
}
That is, instead of using new, just use std::vector<whatever> as if it were any other type.

Related

Initializing a 2D array with operator new

I am trying to declare a 2D array, where each element in the inner array has type uint64_t*. I want to do this in a single line using new, which should be possible in C++11 or later. I tried this:
uint64_t ***db = new uint64_t*[2][64];
However, this is incorrect:
cannot initialize a variable of type 'uint64_t ***' (aka 'unsigned long long ***') with an rvalue of type 'uint64_t *(*)[64]'
Note that the code works correctly when I replace the type with auto.
What is the correct type for the variable?
An array is not a pointer.
On the left you have a pointer to pointers to pointers to integers.
On the right, you are returning a pointer to array of arrays of pointers to integers.
These types do not match.
Now, in C/C++ pointers and arrays are confusing. This is because arrays decay into pointers at the drop of a hat.
Your three star variable can be used to store a jagged array of pointers.
To make a 2d jagged array, you need to both allocate the array of pointers foe the first dimension, then one or more buffers to store the elements. By far the easiest way to do this is a vector of vectors.
Of for whatever reason you insist on using raw pointers, it looks like:
template<class T>
T*const* make_packed2d( std::size_t x, std::size_t y ){
T* inner = new T[x*y];
T**outer = new T*[x];
for (std::size_t i=0; i!=x;++i){
outer[i]=inner+(i*y);
}
return outer;
}
template<class T>
void free_packed2d(T*const* ptr){
if(!ptr) return;
delete[] ptr[0];
delete[] ptr;
}
or similar.
If your array is not jagged, you can do it in one line, but it does not get stored in a three star variable. In the non jagged case, the size of one of the dimensions gets stored in the type.
This type is not compatible with a pointer pointer pointer.
uint64_t *(*ptr)[64] = new uint64_t*[2][64];
the left is the type of variable that can store the new expression you used. I simply copied it out of the error message, and added the variable name in the appropriate spot.
You'll note that the "64" size is hard coded into the variable type. A "jagged" array in C/C++ is an array of pointers to arrays, while a N dimensional array is an array of arrays, where the sub-arrays are fixed in size.
The advantage of "jagged" is that you don't need to know the sub-dimensions as part of the type. The advantage of the non-jagged is that the memory is contiguous, and you don't need the sub-buffers pointing to the sub-arrays.
My make_packed2d attempts to split the difference; it has a sub-buffer pointing to the sub-arrays, but it makes the actual data contiguous and has the sub-buffer pointing to sub-sections of the sub-arrays.
If you want a pointer to an array on heap, you use a pointer to the first element. This means it's the same type as if you would allocate a single element:
uint64_t* p1 = new uint64_t;
uint64_t* p2 = new uint64_t[2];
You should one the second line only use p and p+1, because p+2 is in this example out of range. This goes for all examples below WITH BRACKETS as well.
If you create a pointer to a multidimensional array, you also use a pointer to the first element, but the notation slightly changes. You don't always need the parentheses, but I left them in to see the patterns clearly:
uint64_t (*p1) = new uint64_t; // Optional parentheses.
uint64_t (*p2) = new uint64_t[2]; // Optional parentheses.
uint64_t (*p3)[3] = new uint64_t[2][3]; // Required parentheses.
uint64_t (*p4)[3][4] = new uint64_t[2][3][4]; // Required parentheses.
As you can see, we need parentheses when there are brackets.
And now with pointers:
uint64_t *(*p1) = new uint64_t*; // Optional parentheses.
uint64_t *(*p2) = new uint64_t*[2]; // Optional parentheses.
uint64_t *(*p3)[3] = new uint64_t*[2][3]; // Required parentheses.
uint64_t *(*p4)[3][4] = new uint64_t*[2][3][4]; // Required parentheses.
And finally, BONUS round:
uint64_t **(*p1) = new uint64_t**; // You can leave out the parentheses.
uint64_t **(*p2) = new uint64_t**[2]; // You can leave out the parentheses.
uint64_t **(*p3)[3] = new uint64_t**[2][3];
uint64_t **(*p4)[3][4] = new uint64_t**[2][3][4];
You can use a typedef to simplify the syntax. Then the answer is similar to this answer:
#include <iostream>
typedef uint64_t* uint64_p;
int main() {
uint64_p** db = new uint64_p*[2];
uint64_t num = 5;
db[0] = new uint64_p[64];
db[1] = new uint64_p[64];
db[0][0] = &num;
std::cout << *db[0][0] << std::endl;
return 0;
}
If you want a contiguous block of pointers, you can do this:
#include <iostream>
typedef uint64_t* uint64_p;
typedef uint64_p uint64_p_array[64];
int main() {
uint64_p_array *arr = new uint64_p_array[2];
uint64_t val = 5;
arr[0][0] = &val;
std::cout << *arr[0][0] << std::endl;
return 0;
}
void *ptr;
uint64_t test;
ptr = phys_to_virt(physAddr);
test = *(uint64_t*)ptr;

Passing pointer of multi-dimensional pointer array to a function

ok so suppose I have a function myFunction. Then in main i have a multi dimensional array of pointers. I want to pass a pointer to this array of pointers into myFunction. How would I do that? I know that If you want to pass an int to my function, one can write the function as
myfunct( int x) { ...}
What would that type of x be if I have to pass a pointer to an array of pointers? Thanks in advance :D
Typically you want to modify the elements of an array rather then the actual pointer. The actual pointer is given by malloc and if you change it, by writing directly to the value, it won't affect the memory allocation (except you might loose the initial pointer...).
This might be what you're looking for in a 2D array.
void myfunct(int** ptr,int items, int array_items)
{
//some code
}
int main(int argc, char *argv[])
{
const auto items = 5;
const auto array_items = 7;
int** multi_dimensional_array = reinterpret_cast<int**>(std::malloc(items * sizeof(int*)));
for (auto i = 0 ;i < items;++i)
{
multi_dimensional_array[i] = static_cast<int*>(std::malloc(sizeof(int) * array_items));
}
myfunct(multi_dimensional_array,items,array_items);
//deallocate
}
Wrap your multidimensional array inside a class. That way you can carry the data and dimensions in one block and passing it around is as simple as moving around any other class.
Remember to observe the Rules of Three, Five, and Zero, whichever best applies to how you store your array inside your class. std::vector is a personal favourite because it allows you to use the Rule of Zero.
For example:
#include <iostream>
#include <vector>
struct unspecified
{
};
template<class TYPE>
class TwoDee{
int rows;
int cols;
std::vector<TYPE> data;
public:
TwoDee(int row, int col):rows(row), cols(col), data(rows*cols)
{
// does nothing. All of the heavy lifting was in the initializer
}
// std::vector eliminates the need for destructor, assignment operators, and copy
//and move constructors. All hail the Rule of Zero!
//add a convenience method for easy access to the vector
TYPE & operator()(size_t row, size_t col)
{
return data[row*cols+col];
}
TYPE operator()(size_t row, size_t col) const
{
return data[row*cols+col];
}
};
void function(TwoDee<unspecified *> & matrix)
{
// does stuff to matrix
}
int main()
{
TwoDee<unspecified *> test(10,10);
function(test);
}
To directly answer your question, typically the type passed will be int * for a vector of int, and int ** for a 2D array of int
void myfunct( int **x)
{
x[2][1] = 25;
return;
}
If for some reason you wanted that to be an array of int pointers instead of int you need an extra *.
void myfunct( int ***x)
{
*(x[2][1]) = 25;
return;
}
Let me first try to interpret the exact type that you want to deal with. I suppose in your main function there is a "multidimensional array" which stores pointers for each element. As an example, let's say you have a 3-dimensional array of pointer to integer type.
Assume that you know the size of the array:
C style array will look like this:
int *a[4][3][2];
that means a is a 4x3x2 array, and each element in the array is a pointer to integer. So overall you now have 24 pointers to integer in total, as can be seen by testing the result of sizeof(a) / sizeof(int*) (the result should be 24). Okay, so far so good. But now I guess what you want is a pointer to the array a mentioned above, say b, so b is defined
int *(*b)[4][3][2] = &a;
Notice that although now b looks intimidating, in the end it is just a pointer which just stores an address, and sizeof(b) / sizeof(int*) gives 1 as the result. (The * inside parenthesis indicates b is pointer type, so b is a pointer to a "multidimensional array" of pointers to integer.)
Now to pass b to myFunction, just give the same type of b as argument type in the declaration:
void myFunction(int *(*x)[4][3][2]) {
// do something
}
And that's it! You can directly use myFunction(b) to invoke this function. Also, you can test that inside myFunction, x is still of the size of one pointer, and *x is of the size of 24 pointers.
*Note that since we are passing a pointer to array type into the function, the array-to-pointer decay does not apply here.
Assume you don't know the size of the array at compile time:
Say you have int N1 = 4, N2 = 3, N3 = 2; and you want to initialize a N1xN2xN3 array of pointer to integer, you cannot directly do that on the stack.
You could initialize use new or malloc as suggested in #Mikhail's answer, but that approach takes nested loops for multidimensional arrays and you need to do nested loops again when freeing the memory. So as #user4581301 suggests, std::vector provides a good wrapper for dynamic size array, which do not need us to free the memory by ourselves. Yeah!
The desired array a can be written this way (still looks kind of ugly, but without explicit loops and bother of freeing memory)
std::vector<std::vector<std::vector<int*>>> a (N1,
std::vector<std::vector<int*>> (N2,
std::vector<int*> (N3)
)
);
Now, b (the pointer to a) can be written as
auto *b = &a;
You can now pass b with
void myFunction(std::vector<std::vector<std::vector<int*>>>* x) {
// do something
}
Notice that the * before x means x is a pointer.

Const vector query in C++

#include < vector >
using namespace std;
class Rclass
{
public:
vector<int> ir0T;
vector<int> ir1T;
private:
int f();
}
int Rclass::f()
{
ir0T.clear();
ir1T.clear();
ir0T.push_back(1);
ir1T.push_back(2);
}
this throws error
"Rclass.cpp:90: error: member function 'clear' not viable: 'this' argument has type 'const vector', but function is not marked const
ir0T.clear();
^~~~"
Rclass.cpp:91: error: member function 'clear' not viable: 'this' argument has type 'const vector', but function is not marked const
ir1T.clear();"
why?
^~~~
I tried adding "const vector ir0T;"
You cannot set the matrix member variable to a local varable created in a local member function - the local variable will be destroyed when the function ends and then the matrix member variable won't be pointing to anything. So instead, if you insist on using a raw pointer, use calloc() because it allocates the memory like malloc and then it sets it all to zero. The main problem with this is that then you need a copy constructor, assignment operator and destructor - That's not the way to go if you can help it. It would be better to use a std::vector<std::vector<int>> because all the dynamic allocation and deallocation is hidden from you. Plus you can reserve the size if you know it ahead of time. How to initializ the "vector"-ized version to zero can be seen here: Initializing a two dimensional std::vector
#include <vector>
class CS
{
private:
std::vector<std::vector<int> > Rightalpha;
public:
void CreateMtrx(int a, int b)
{
// Defaults to zero initial value
Rightalpha = std::vector<std::vector<int> >(a, std::vector<int>(b));
}
};
int main()
{
CS cs;
cs.CreateMtrx(4,4);
return 0;
};
A better alternative if it is fixed and you know ahead of time how big the matrix is: you can just use a plain array directly as a member variable instead of using a pointers to dynamically allocated memory. If the matrix is small (like 4x4) this will give you cache locality and a performance improvement. Plus if you are using c++11 you can clear the array at the declaration and you don't need a CreateMatrix() member variable at all - something like this:
class CS
{
private:
int Rightalpha[4][4] = {};
};
int main()
{
CS cs;
return 0;
};
Or like one of the comments suggested you could use std::array instead of a plain array, if you want a nice STL-like interface to the array. There are some advantages listed here: Replace fixed size arrays with std::array?
Firstly a few fundamentals.
When CreateMtrx() returns Rightalpha will become invalid as a will destruct.
And I would recommend using lower camel case naming for variables and upper camel case for types. i.e. rightAlpha instead of Rightalpha, to avoid confusion with types.
As for your actual question you can initialise a 2D array with a nested loop:
for(unsigned int i = 0; i < 4; i++)
{
for(unsigned int j = 0; j < 4; j++)
{
rightAlpha[i][j] = 0;
}
}
Finally, when asking for help 'craps up' is not conducive to constructive answers. It is important to be clear on what your expected behaviour is and what results you are actually seeing.
If Rightalpha is a data member of your class it doesn't need to be an int**. You probably just want it to be an int[4][4] and skip using a local variable 'a' in your create function.
If you really want it to be a pointer, just make it an int*, and use it with 2D-array syntax. Instead of: int a[4][4]; Do: int* a = new [4*4];
I see from the comment that you can't change the type of Rightalpha. You will then need to do manual memory management. You will need to initialize you int** with the new operator.
You will need to allocate each array in the 2D array.
rightAlpha = new int*[4];
for (int i = 0 ; i < 4 ; i++) {
rightAlpha[i] = new int[4];
}
You can read more about initialisation of a multi-dimentional arrays here:
How do I declare a 2d array in C++ using new?
Even if that works, you will need to free and manage memory and deal carefully with all the pitfalls of manual memory management. That's why I strongly suggest to use a std::vector<int>:
struct CS {
createMatrix() {
rightAlpha = std::vector<int>(4*4);
}
private:
std::vector<int> rightAlpha;
With this solution, you don't need to worry about memory stuff as the std::vector will do it for you.
If you need matrix semantics, you can add a function that returns the right element according to a j i position.
int operator()(int i, int j) const {
return rightAlpha[j+4*i];
}
It may be used like this:
CS myCs;
myCs(3, 2);

c++ allocate array syntax question

You can write like this:
int test[] = {1,2,3,4};
but what if you want to use a pointer and allocate it with new?
int *test2;
test2 = new int[]{1,2,3,4};
This does not work, what is the syntax or is it possible?
This is one of the short-comings of current standard C++. Initialization is not uniform. Your best hope is uniform initialization in C++0x, but for the mean while you can assign the values after creating the array, e.g.
int *test2;
test2 = new int[4]; // Note that you still have to specify the size in C++0x
test2[0] = 1;
test2[1] = 2;
test2[2] = 3;
test2[3] = 4;
If you compiler supports C++0x
int *test2;
test2 = new int[4] {1,2,3,4}; // Initializer list in C++0x
would work. However you should always use std::vector instead of C style arrays while writing good C++ code.
The first syntax is called aggregate initialization, and you cannot apply it to a dynamically allocated array. When allocating dynamically you must provide the number of elements that you want to initialize inside the square brackets and (optionally) the default value in parenthesis (if you want the array initialised). The default value will be the same (if present) for all elements.
You may want to look into the boost::assign library that may have support for this type of initialization (not sure). Alternatively you can (at the cost of more code) do it yourself for POD types:
int * array = new int[4];
{
int values[] = { 1,2,3,4 };
memcpy( array, values, sizeof(values) );
}
Or for non-pod types:
type * array = new type[4];
{
type values[] = { 1,2,3,4 }; // assuming type(int) constructor
std::copy( values, values+4, array ); // better to use some magic to calculate size
}
At any rate both of those solutions require allocating locally and the copying (bit-size/c++) into the dynamically allocated memory.

The right way to create pointer to pointer object?

What is the right way to create a pointer to pointer object? Like for example,
int **foo;
foo = new int[4][4];
Then the compiler gives me an error saying "cannot convert from int (*)[4] to int **.
Thanks.
int **foo = new int*[4];
for (int i = 0; i < 4; i++)
foo[i] = new int[4];
Clarification:
In many languages the code above is called a jagged array and it's only useful when the "rows" have different sizes. C++ has no direct language support for dynamically allocated rectangular arrays, but it's easy to write it yourself:
int *foo = new int[width * height];
foo[y * height + x] = value;
Using raw new is a bit unwieldy to use. The inner dimension (last 4) must be a compile time constant, in addition. You also have to remember to delete the array once you are finished using it.
int (*foo)[4] = new int[4][4];
foo[2][3] = ...;
delete[] foo;
If that feels too "syntactic braindead", you can use typedef to prettify it
typedef int inner_array[4];
inner_array *foo = new int[4][4];
foo[2][3] = ...;
delete[] foo;
That's called a rectangular 2-dimensional array, because all the rows (4 of them, which can be determined at runtime) have the same width (which must be known at compile time).
Alternatively, use std::vector, with which you don't need to mess around with delete anymore, and which will also handle the raw pointer mess:
std::vector<int> v(4 * 4);
v[index] = ...;
You may later add or remove integers from the vector as you wish. You could also create a vector< vector<int> >, but i find it unwieldy to use, because you have to manage the separate row-vectors (which can be of varying lengths), and they are not seen as "one unit" together.
You can always create a function that maps a two dimensional coordinate to a one-dimensional index
inline int two_dim(int x, int y) {
return y * 4 + x;
}
v[two_dim(2, 3)] = ...;
For a simple two-dimensional array whose size you know beforehand, you don't need new at all, though
int x[4][4];
x[2][3] = ...;