Simulate 2D array to 1D [duplicate] - c++

This question already has answers here:
Representing a 2D array as a 1D array [duplicate]
(5 answers)
Closed 9 years ago.
I have a jagged 2D array, the rows aren't all the same length:
int sizes[100];
//init sizes
unsigned char *p = [100];
for(unsigned int i = 0; i < 10; i++)
{
p[i] = (unsigned char*)malloc(sizeof(char)*sizes[i]);
for(unsigned int j = 0; j < sizes[i]; j++)
p[i] = j;
}
I use the array like this:
p[x][y]
How can I simulate this array to 1D?

I assume that if you want to access your "2D array" as a one D matrix, you expect that as you increment your index by 1, you access the next element in the array (and will automatically go to the next line when you run off the edge). The right way to do this is by changing the way you allocate the array. I'll try to show how this is done - this is just C, not C++. It's probably more appropriate since you were using malloc anyway. I am also thinking that you have a pretty serious error in your code, in that you are creating a char * pointer p, yet expect to use it in
p[x][y];
for which you would need a char **, obviously. Let's try to make code that would do what you want:
int sizes[100];
//init sizes
unsigned char *p[100]; // without the == sign we create an array of 100 pointers
unsigned char *bigP; // the master pointer
int totalLength = 0;
int ii;
for(ii=0; ii<100; ii++) totalLength += sizes[ii];
bigP = malloc(sizeof(char) * totalLength);
int offset = 0;
// make pointers p point to places along this big memory block:
for(ii = 0; ii < 100; ii++) {
p[ii] = bigP + offset;
offset += sizes[ii];
}
Now you can either address your array with
p[x][y];
or with
bigP[z];
where z can go from 0 to the maximum number of elements. Of course, in the process you don't know (when you are using the "1D" paradigm) in what row/column of the jagged array you are - you can't know that if you are truly in one dimension.
The key here is that the memory is allocated as a single contiguous block, and the pointers p are pointing to places in that block. This means you MUST NOT FREE p. You must only ever free bigP.
I hope this makes sense.

If you're looking for a way to map a two dimensional array onto a one dimensional space, then try...
int sizes[width*height];
void setPoint(int x, int y, int val) {
sizes[x*width + y] = val;
}
Notably the x*width + y indexing will give you the appropriate element in the one dimensional array.

Unless it is homework just download boost and use Boost.Matrix.

Related

Creating memory space for multi-dimensional array

I am studying c++ and found this in the book
array_ptr = new int[3][5][4];
This is used to allocate memory to a multi dimensional array using new. It also states while first dimension can be a variable whose value is supplied at runtime, others must be constant.
I tried to run this code
int *p = new int[3][5][6];
But it is showing an error.
Can someone elaborate.
Here is what you want:
int (*p)[5][6] = new int[3][5][6];
Since C++11 you can ask the compiler to decuce the type of p for you:
auto p = new int[3][5][6];
You don't need anything special to delete such array:
delete [] p;
Logic behind the choice of the type is simple:
In this line: int *p = new int[10] you're creating an array of 10 ints and you use pointer to int to store the address.
In general case, if you're allocating an array of N Ts (where N is the size and T is the type), you use pointer to T to store the address.
Same rule can be applied to multidimensional arrays, because they are in fact nested 1D arrays.
When you try to allocate multidimensional array: p = new int[4][5][6], you're creating an array of 4 arrays of 5 arrays of 6 ints. Thus you need a pointer to array of 5 arrays of 6 ints, which is int (*p)[5][6].
In other words, int [3][4][5] can be viewed as 1D array of size 3. It consists of other arrays of type int [4][5]. Each of these arrays has size 4 and consists of other arrays of type int [5], which in turn contain 5 ints.
P.S. Authors of other answers seem to favor using int *** and allocating memory for each nested array separately. Doing so may seem smart, but in fact it's much slower and dangerous (if you mess up your memory management). The only advantage of that trick is that it provides the convenient interface we all used to (p[z][y][x]).
But there is a much better solution: int *p = new int[x_sz * y_sz * z_sz];. You would need to convert 3D indices to 1D index (p[x + x_sz * (y + z * y_sz)] instead of p[z][y][x]) for it to work, but in my opinion it's still more convenient (and definitely faster).
Of course, in real code, you should use std::vector to store such array and write your own wrapper class, which would compute the index automatically.
A 3d array is not the same as a pointer to int, and you can't make them the same.
If you need dynamic allocation, you can use special class std::vector (best choose), std::array, or write next code:
int ***p = new int**[3];
for (int i = 0; i < 3; ++i) {
p[i] = new int*[5];
for (int j = 0; j < 5; ++j) {
p[i][j] = new int[6];
}
}
In order to create dynamic 3d array you have to use pointer to pointer to pointer)
const int xSize = 3;
const int ySize = 5;
const int zSize = 4;
int*** p = nullptr;
// creating array of pointers to pointers
p = new int** [xSize]
// for each pointer to pointers, create array of pointers
for( int i = 0; i < xSize; ++i )
p[ i ] = new int* [ySize];
// now we have 2d array of pointers, for each of them allocate array
for( int i = 0; i < xSize; ++i )
for( int j = 0; j < ySize; ++j )
p[ i ][ j ] = new int [zSize];
But is much more better to use vectors
vector<vector<vector<int> > > v3d;
v3d.resize( 3 );
for( int i = 0; i < xSize; ++i )
v3d[ i ].resize( ySize );
for( int i = 0; i < xSize; ++i )
for( int j = 0; j < ySize; ++j )
v3d[ i ][ j ].resize( zSize );

Can't declare dynamic 2D array in C++ [duplicate]

This question already has answers here:
How do I declare a 2d array in C++ using new?
(29 answers)
Closed 8 years ago.
I've stuck on a problem - I can't declare 2D arrays in C++ using integers, written by user.
This code works fine-
cin>>m>>n;
int *array;
array=new int[m*n];
But I can't make this work -
cin>>m>>n;
int *array;
array=new int[m][n];
Any ideas how i can bypass it?
P.S. the error : cannot convert 'int ()[2]' to 'int' in assignment.
Change
cin>>m>>n;
int *array;
array=new int[m][n];
to
cin>>m>>n;
int **array;
array=new int * [m];
for ( int i = 0; i < m; i++ ) array[i] = new int[n];
array is int * and you try to assign int **... change array to int**.
2D array is actually array of arrays, so you need pointer to pointer.
That's because you can only use new to allocate 1D arrays. In fact, 2D arrays are also 1D arrays, where, in most systems, all rows are simply concatenated. That is called a row-major memory layout.
You can emulate 2D arrays with a 1D array. The index conversion is:
index1 = y * m + x
This also has much better performance than creating one array per row, as recommended in the "duplicate" link or in other answers.
Just as Domi said (but not index1=y*m+x but rather index1=x*n+y to emulate your desired notation):
int *array = new int [m*n];
int get (int x, int y) // emulates array[x][y] for array[m][n]
{
return array[x*n+y];
}
However, I think the real 2-dimensional allocation (as Vlad from Moscow showed you) is slower in creation (and needs a bit more memory), but quicker in accessing. Cause array[x*n+y] == *(array+x*n+y), wether array[x][y] == *(*(array+x)+y), so you have one multiplication less, but one dereferenciation more, in sum I think it's quicker.
You could also create a class:
class array2d
{
private:
int *data;
int mm, nn;
public:
array2d (int m, int n)
{
mm = m;
nn = n;
data = new int [m*n];
}
~array2d ()
{
delete[] data;
}
int *operator[] (int x)
{
return (data+x*nn);
}
};
With it you can use
array2d arr(10,10);
arr[5][7] = 1;
You can do this:
typedef int RowType[n];
RowType *array = new RowType[m];
doStuffWith(array[y][x]);
Or, even shorter (but harder to remember):
int (*array)[n] = new (int[m][n]);
Edit:
There is a catch in C++ that array sizes must be constant for the new operator, so you can only do this if n is const. This is not a problem in C (and the reason I forgot about this), so the following works even if n and m are not const:
RowType *array = (RowType*)malloc(m * sizeof(RowType));
Of course, you can work around this restriction in C++ by doing this, which works even if both m and n are dynamic:
RowType *array = (RowType*)new int[m * n];
The typedef free version would be this:
int (*array)[n] = (int (*)[n])new int[m *n];
Firstly I suggest you should use std::vector to avoid memory allocation / deallocation issues.
In case you want an array implementation, then you can declare array as a pointer to pointer to int.
cin >> m >> n;
int** array = new int* [m];
for ( int I = 0; I < m; I++ ) {
array[I] = new int[n];
}

Allocate a multidimensional array without recursion

Suppose I have an arbitrary size array of integer values that specify the number of elements for each dimension (level) of the array to be allocated, how do I allocate the array without resorting to recursion? It's preferable to do it without recursion to avoid stack overflow.
So, for example, how to complete a function like this:
template <typename Type>
void* allocMulti (int numDim, int* numElementsPerDim)
{
// 'Type' if one-dimensional, should be 'void*' otherwise
void* multiArray = new Type[numElementsPerDim[0]];
// ...
return multiArray;
}
I'm looking for a general algorithm that would cover languages without direct memory access.
If the array is actually a matrix (e.g. length AxB and not a list of arrays of different lengths), then you could allocate a single array of length A*B instead of an array of length A where each position is a pointer to an array of length B.
This could also improve performance, as the memory is continuous (less paging).
You would have to access each cells using a[y * B + x] instead of a[y][x] though (assuming dim(a,0) = A and dim(a,1) = B.
My C++ my be a bit rusty, however, I believe this sort of approach may work:
T* AllocateMatrix(int dims, int[] dimLengths)
{
// Assert dims >= 1
int length = dims[0];
for (int d = 1; d < dims; d++)
length *= dims[d];
return new T[length];
}
*T AccessMatrix(T* matrix, int dims, int[] dimLengths, int[] pos)
{
// Assert dims >= 1
int p = pos[0];
for (int d = 1; d < dims; d++)
{
p = p * dimLengths[d] + pos[d];
}
return &matrix[p];
}
Here's an approach: Allocate the data values as a block, then all the rows of (int *) as a block, then the rows of (int **) as a block, etc.
a) Allocate all the data values as a block. If you have nDim dimensions in the array elementsPerDim, there are prod = product(elementsPerDim, nDim) data values (which you can easily calculate), so you need to allocate:
int prod = product(elementsPerDim, nDim);
int * intblock = calloc(prod, sizeof(int));
b) Allocate all the (int*). Their number is equal to the product of all the dimensions except the last one, so you can simply call your product() function with length nDim-1. So there are product(elementsPerDim, nDim-1) such values, each of size sizeof (int*). Let's allocate them:
int npointers = product(elementsPerDim, nDim-1);
int ** ptrblock = calloc(npointers, sizeof (int *));
Now you must initialize them to point into your block from the previous step. Each pointer gets a non-overlapping block of elementsPerDim[nDim-2] ints, like this:
int rowlength = elementsPerDim[nDim-2];
for (int i=0; i < npointers; i++)
ptrblock[i] = & intblock[i * rowlength]; /* a.k.a. intblock + i*rowlength */
c) Iterate step b backwards until you run out of dimensions. I.e., follow up step (b) with this loop:
void ** prev_block = (void **) ptrblock;
void ** curblock;
for (int d = nDim-2; d > 0; d--) {
int npointers = product(elementsPerDim, d);
curblock = calloc(npointers, sizeof (void **));
int rowlength = elementsPerDim[d-1];
for (int i=0; i < npointers; i++)
curblock[i] = & prev_block[i * rowlength];
prev_block = curblock; /* get ready for the next round */
}
When you're done, curblock will be an array of pointers pointing into the block of second-level pointers, and so on down to the block of ints. You can use normal array notation to dereference them:
ptrblock[3][2][15], etc.
I may have gotten an index off by one somewhere, but this should be the algorithm. You'll notice this is in C, and uses void ** instead of stacking the number of dereferences. You did say you were interested in the algorithm, not in type golf... (It should work as long as all pointers have the same size on your machine.)

c style string and dynamic allocation in c++

I must implement a bunch of methods that allocates, modify and free a 2D array of c-style string. I cannot use string nor vectors nor any stl container.
getNewMat :
char*** getNewMat(int w, int h){
char*** newMat = new char**[h];
for(int i = 0 ; i < h ; i++){
newMat[i] = new char*[w];
for(int j = 0 ; j < w ; j++)
newMat[i][j] = NULL;
}
return newMat;
}
fillMat
void fillMat(char***mat, int x, int y, char* newEl){
mat[y][x] = newEl; //this will produce a segfault (even with good index)
}
showMat :
void showMat(char*** mat, int w, int h){
for(int i = 0 ; i < h ; i++){
for(int j = 0 ; j < w ; j++)
cout << mat[i][j];
}
cout << endl;
}
so, can anyone please tell me what's wrong with this?
In your fillMat method you do this:
mat[y][x] = newEl;
Where x and y are the dimensions of the two ranks of the array. That line will cause a segmentation fault because you're going outside the bounds of the array. mat is indexed from 0 to length - 1 and setting by x and y is going 1 outside the bounds of the array.
Maybe you meant to loop through and set them:
for (int i = 0; i < y; ++i)
{
for (int k = 0; k < x; ++k)
mat[i][k] = newEl;
}
Moreover, inside your showMat function you have this:
cout << showMat[i][j];
I think you meant for that to be mat:
cout << mat[i][j];
newMat[i][j] = NULL - it's a bad idea. In showMat you will try to dereference a NULL pointer - this is UB and may cause a segfault.
char* - it's not a string - it's just a pointer to char, that may points to memory location, where can be beginning of string. If you want to work with it like with a string, you should allocate memory for it too.
mat[y][x] = newEl - it's a bad idea too. As I already said, char* is not a string, so, you can't just use assignment operator to copy data from one C-string into another. You should use std::copy or std::strncpy.
Do not forget to free allocated memory after using.
You should implement your own string class - it's the better solution, I can see there. At least, because it simpler and easier to understand.
I must implement a bunch of methods that allocates, modify and free a
2D array of c-style string. ...snip... so, can anyone please tell me
what's wrong with this?
A "c-style string" isn't a type. It's a representation of data within a type. '\0'-terminated strings are typically stored within char arrays, but you could store one just as easily in a unsigned int array. For example:
unsigned int message[32] = { 0 };
strcpy((char *) message, "Hello, world!");
printf("%s\n", (char *) message);
I discourage programming like this, however micro-optimistic the benefits may seem. It's also possible to store a string in something that isn't an array. Consider that a char might be suitable for storing an empty string:
char x = '\0';
printf("%s\n", &x);
It's reasonable to assume that you meant "an array of array of array of char", when you said "2D array of c-style string". Let us carry on in that direction.
I don't know a lot about C++, but there's a list of property of arrays which you probably haven't thought about in your quest to mimic actual arrays. I'll summarise these using assertions:
#define x 7
#define y 13
#define z 1
char foo[x][y][z] = { 0 };
assert((char *) foo == foo[0]);
assert(sizeof foo == (char *) &foo[1] - (char *) &foo[0]);
assert(sizeof foo == x * y * z);
I'm not sure if you'll be able to solve your problem with any of these assertions passing in C++, but I'm open for any input from others as to hints as to how one might...
Arrays are contiguous. This means that newMat[x] + w and newMat[x+1], for values of x in 0 .. h-1. In your code, this isn't a reality, because you allocate newMat[x] and newMat[x+1] separately. Similarly, it is expected that newMay[0][y] == newMat[0][y+1] + n, where n is the maximum length of your strings. This can be a problem when using generic array sorting algorithms, because they might rely upon your arrays being contiguous.
The closest you might come to solving this problem seems to involve allocating only once per dimension, rather than h times for the first dimension and w for the second. This would look something like this:
char ***getNewMat(size_t w, size_t h, size_t n){
char ***newMat = new char **[h];
newMat[0] = new char *[h*w];
newMat[0][0] = new char[h*w*n];
for(size_t i = 0; i < h; i++){
newMat[i] = newMat[0] + i * w;
for (size_t j = 0; j < w; j++) {
newMat[i][j] = newMat[0][0] + i * w * n + j * n;
}
}
return newMat;
}
One side-effect of arrays being contiguous is that you can't assign C-style strings by merely changing the pointer within the array to point to a different location. The pointer is the result of a conversion from an array expression to a pointer expression which isn't an lvalue. As I said earlier, I don't know much about C++, but in C that means the following code can't compile:
char foo[x][y][z] = { 0 };
foo[a][b] = "hello";
However, the following code can compile:
char *foo[x][y] = { 0 };
foo[a][b] = "hello";
The former might constitute an array of C-style strings, but the latter can't because of the contiguity rule we've covered, and the fact that it starts off with most if it's elements pointing to NULL, a pointer which can't point to anything let alone strings. There might be some operator overloading magic you can perform to permit the former to compile. I'm also open for any hints in the right direction to provide an example for the OP, here.

C++ two dimensional arrays with pointers

I have a problem with two dimensional arrays :( I feel very stupid and Visual C does not help me :( and I also think that my mistake is very stupid but still I can't find it :( I have this code:
double matrix[100][100]; //which is full with a matrix 3x4
double nVector[10000]; // for negative doubles
//I wanted to see if there are negative doubles in each row and column
//and I want this to happen with function
And this is my function:
double* negativeVector(double*nVector, double*fromVector, int m, int n){
int position = 0;
double *myNegArray = nVector;
double *myMatrix = fromVector;
for(int i = 0; i < m*n; i++)
if(*(*(myMatrix+i)) < 0){
*(myNegArray+position) = *(*(myMatrix+i));
position++;
}
return myNegArray;
}
//for double*nVector I'm passing nVector
//for double*fromVector I'm passing *matrix
Visual C tells me that I have an error C2100: illegal indirection here: *(*(myMatrix+i)) I hope someone can help me (happy)
Thanks in advance!
*(*(myMatrix+i)) is wrong. This is a common mistake.
2D matrix does not create an array of pointers which you can access this way. It is a different structure. Even though an array is a pointer, 2D array is not a pointer to pointer, and it cannot be dereferrenced twice. Nor you have any other way to access element at coordinates (x,y) without knowing the layout in memory, because pointers to every line are nowhere to be found. For instance, char **argv parameter of main() is not a 2D array. This is an array of pointers to arrays, which is something else.
There're two ways to fix it.
One is replace
double *myMatrix = fromVector;
by
double *myMatrix[100] = (appropriate cast)fromVector;
and index it as myMatrix[i/n][i%n]
But then remember that 100 is a constant expression, and it cannot be passed as a parameter. Alternatively, you can implement the indexing operation yourself:
Pass additional parameter: matrix line size (100)
Instead of *(*(myMatrix+i)), write:
int row = i/n;
int col = i%n;
*(myMatrix+row*line_size+col) is your element.
first you might wanna start a small struct like
struct tmp {
bool negative;
double value;
};
and make your own way up to the
tmp *myvars [100][100];
.
instead try using that struct and try the std::vectors instead of arrays if that's possible then try using pointers on decalring the variable "1 time only" when declaring the variable as i said above
then pass arguments
( tmp *mystructpointer )
mystructpointer->.......
access your matrix directly ... peice of cake :D
If you are passing *matrix, you are actually passing a double[100] (an array of 100 doubles), that happens to be passed as a pointer to its first element. If you advance further than those 100 doubles using i added to that pointer, you advance into the next array of 100 doubles, since the 100 arrays of 100 doubles are stored next to each other.
Background: A multi-dimensional array is an array whose element type is itself an array. An array like double a[100][100]; can be declared equivalently as typedef double aT[100]; aT a[100];. If you use an array like a pointer, a temporary pointer is created to the array's first element (which might be an array). The * operator is such an operation, and doing *a creates a pointer of type double(*)[100] (which is a pointer to an array of 100 doubles), and dereferences it. So what you end up with *matrix is a double[100]. Passing it to the negativeVector function will create a pointer to its first element, which is of type double*.
Your pointer parameters point to the start of each of two arrays of 100 doubles each. So you should rewrite the function as
double* negativeVector(double*nVector, double*fromVector, int m, int n){
int position = 0;
double *myNegArray = nVector;
double *myMatrix = fromVector;
for(int i = 0; i < m*n; i++)
if(*(myMatrix + i) < 0){
*(myNegArray + position) = *(myMatrix + i);
position++;
}
return myNegArray;
}
Notice that since your i iterates beyond the first of the 100 arrays stored in the 2d array, you will formally not be correct with this. But as it happens those arrays must be allocated next to each other, it will work in practice (and in fact, is recommended as a good enough work around for passing multi-dimensional arrays around as pointers to their first scalar element).
I have no clue why you are copying the arrays twice (once in the parameters of the function and a second time by declaring some new arrays)... You should also think of using the STL... std::vector will make the your life way easier ;)
double* negativeVector(double*nVector, double*fromVector, int m, int n){
int position = 0;
double *myNegArray = nVector;
double *myMatrix = fromVector;
for(int i = 0; i < m*n; i++)
if(*((myMatrix+i)) < 0){
*(myNegArray+position) = *((myMatrix+i));
position++;
}
return myNegArray;
}
is that homework? some templates - just for fun ;-)
double matrix[100][100];
double nVector[10000];
template< const int m, const int n >
double* negativeVector( double* myNegArray, const double (&myMatrix)[m][n] )
{
int position = 0;
for( int i = 0; i < m; ++i )
{
for( int j = 0; j < n; ++j )
{
const double value = myMatrix[ i ][ j ];
if ( value < 0 )
{
myNegArray[ position ] = value;
++position;
}
}
}
return myNegArray;
}
int main()
{
//...initialize matrix here...
negativeVector( nVector, matrix );
}
Perhaps rewrite this using std::vector to increase readability? (#):
#include <vector>
std::vector< std::vector<double> > matrix; //which is full with a matrix 3x4
std::vector<double> row;
row.resize(100,0);
matrix.resize(100,row);
std::vector<double> nVector; // for negative doubles, no size, we'll "push_back"
//I wanted to see if there are negative doubles in each row and column
//and I want this to happen with function
This is the stl enabled version of the function:
//I'm returning void because nvector contains the result,
//so I don't feel the need to return anything. vectors contain their
//own size so n and m are also not needed. Alsom pass in references
void negativeVector(std::vector<double>& nVector,
std::vector< std::vector<double> >& fromVector){
nVector.clear();
int i,j;
for(i = 0; i < fromVector.size(); i++) {
for(j = 0; j < fromVector[i].size(); j++) {
if(fromVector[i][j] < 0){
nVector.push_back(fromVector[i][j]);
}
}
}
}
call with:
negativeVector(nVector, matrix);
Once the function completes, nVector contains all negative numbers in matrix.
Read more about std::vector here.
(#) for people like me who are too lazy/stupid to comprehend code containing pointers.
Take a look at C++ Faq site:
How do I allocate multidimensional arrays using new?
link
And read until point [16.20] summarize all the answers you are getting and at the end you get a very useful Matrix template class.
Have a good read.