I was programming a dynamic array for my own use, that i wanted pre-set with zeros.
template <class T>
dynArr<T>::dynArr()
{
rawData = malloc(sizeof(T) * 20); //we allocate space for 20 elems
memset(this->rawData, 0, sizeof(T) * 20); //we zero it!
currentSize = 20;
dataPtr = static_cast<T*>(rawData); //we cast pointer to required datatype.
}
And this part works - iterating by loop with dereferencind the dataPtr works great. Zeros.
Yet, reallocation behaves (in my opinion) at least a bit strange. First you have to look at reallocation code:
template <class T>
void dynArr<T>::insert(const int index, const T& data)
{
if (index < currentSize - 1)
{
dataPtr[index] = data; //we can just insert things, array is zero-d
}
else
{
//TODO we should increase size exponentially, not just to the element we want
const size_t lastSize = currentSize; //store current size (before realloc). this is count not bytes.
rawData = realloc(rawData, index + 1); //rawData points now to new location in the memory
dataPtr = (T*)rawData;
memset(dataPtr + lastSize - 1, 0, sizeof(T) * index - lastSize - 1); //we zero from ptr+last size to index
dataPtr[index] = data;
currentSize = index + 1;
}
}
Simple, we realloc data up to index+1, and set yet-non-zeroed memory to 0.
As for a test, i first inserted 5 on position 5 on this array. Expected thing happened - 0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Yet, inserting something else, like insert(30,30) gives me strange behavior:
0, 0, 0, 0, 0, 5, 0, -50331648, 16645629, 0, 523809160, 57600, 50928864, 50922840, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30,
What the hell, am i not understanding something here? shouldnt realloc take all the 20 previously set memory bytes into account? What sorcery is going on here.
Problem 1:
You are using the wrong size in the call to realloc. Change it to:
rawData = realloc(rawData, sizeof(T)*(index + 1));
If rawData is of type T*, prefer
rawData = realloc(rawData, sizeof(*rawData)*(index + 1));
Problem 2:
The last term of the following is not right.
memset(dataPtr + lastSize - 1, 0, sizeof(T) * index - lastSize - 1);
You need to use:
memset(dataPtr + lastSize - 1, 0, sizeof(T) * (index - lastSize - 1));
// ^^ ^^
// size * The number of objects
Problem 3:
Assigning to dataPtr using
dataPtr[index] = data;
is a problem when memory is obtained using malloc or realloc. malloc family of functions return just raw memory. They don't initialize objects.
Assigning to uninitialized objects is a problem for all non-POD types.
Problem 4:
If T is type with virtual member functions, using memset to zero out memory will most likely lead to problems.
Suggestion for fixing all the problems:
It will be much better to use new and delete since you are in C++ land.
template <class T>
dynArr<T>::dynArr()
{
currentSize = 20;
dataPtr = new T[currentSize];
// Not sure why you need rawData
}
template <class T>
void dynArr<T>::insert(const int index, const T& data)
{
if (index < currentSize - 1)
{
dataPtr[index] = data;
}
else
{
const size_t lastSize = currentSize;
T* newData = new T[index+1];
std::copy(dataPtr, dataPtr+lastSize, newData);
delete [] dataPtr;
dataPtr = newData;
dataPtr[index] = data;
currentSize = index + 1;
}
}
Please note that the suggested change will work only if T is default constructible.
This will also take care of the problems 3 and 4 outlined above.
Related
I need to create a lot of small 2-dimension arrays in C++ code.
The problem is that it's a lot of work to create even a simple array:
new int* [2]{
new int[2]{9, 9},
new int[2]{25, 19}
};
Is there any better way how to do that?
I wanted to avoid writing "new int[]..." every time.
If the dimensions are not decided at runtime, and all the inner arrays have the same dimensions, you do not need dynamic allocation here.
Just declare an array:
int myArray[2][2] = {
{9, 9},
{25, 19}
};
That's it!
I recommend allocating as a single dimension array. You can then treat the 1D array as a 2D array:
const unsigned int MAX_ROWS = 2U;
const unsigned int MAX_COLUMNS = 5U;
int example_array[MAX_ROWS * MAX_COLUMNS];
// Get value at [row][column]:
unsigned int one_dim_index = (row * MAX_COLUMNS) + column;
int value = example_array[one_dim_index];
For small array sizes, this would be more efficient since the processor can fit the entire contiguous array in the data cache. With your solution, an array of pointers, you have no idea where the sub-arrays are located and they may not be contiguous (thus requiring a refetch into the cache).
Edit 1: Initializing
You can initialize the array by making the rows and columns pretty:
int example_array[MAX_ROWS * MAX_COLUMNS] =
{
/* row 0 */ 1, 2, 3, 4, 5,
/* row 1 */ 6, 7, 8, 9, 10,
};
Maybe you can use nested for loops to do the task
const int ARRAY_SIZE = 2;
int **create_array() {
int **array = new int*[ARRAY_SIZE];
if (array == nullptr) { return nullptr; }
for (int i=0; i<ARRAY_SIZE; i++) {
array[i] = new int[ARRAY_SIZE];
if (array[i] == nullptr) { return nullptr; }
}
return array;
}
If you want to assing the values you can do it directly in here. But they should come from a function of i. If you want some really specific values it will need to be a manual job
recently moved from C# to C++ so I'm new to pointers and references and so on.
I've a pointer-to-pointer array declared like this
enum Type
{
Void,
DeepWater,
Water,
... etc }
Tile::Type** tiles;
TileManager::TileManager(int width, int height)
{
this->tiles = new Tile::Type*[width];
for (int w = 0; w < width; w++)
{
tiles[w] = new Tile::Type[height];
for (int h = 0; h < height; h++)
{
tiles[w][h] = Tile::Type::Dirt;
}
}
}
Now I'm putting together a method that returns the neighbours of a cell in the tiles array and checking if each neighbour is not-equal to NULL.
However even when checking whether it's null or not seems to throw an error, so I'm stumped.
Tile::Type * TileManager::GetNeighbours(int x, int y)
{
Tile::Type neighbours[8];
if(tiles[x][y+1] != NULL)
neighbours[0] = tiles[x ][y + 1];
...etc
if (tiles[x - 1][y - 1] != NULL) //<-- Error fires here
neighbours[5] = tiles[x - 1][y - 1];
return neighbours;
}
I know why it's throwing the error but shy of checking X and Y to see if they go over the limit or below 0... I figure there's a more practical way to prevent this so thought I'd best ask.
Edit:
Thank you, user4581301. I found most of this code elsewhere and adapted it to reflect the changes you suggested.
std::array<Tile::Type, 8> TileManager::GetNeighbours(int c, int r)
{
std::array<Tile::Type, 8> neighbours;
const int y[] = { -1, -1, -1, 1, 1, 1, 0, 0 };// 8 shifts to neighbors
const int x[] = { -1, 0, 1, -1, 0, 1, -1, 1 };// used in functions
for (int i = 0; i < 8; ++i)// visit the 8 spaces around it
if (inField(r + y[i], c + x[i]))
neighbours[i] = tiles[r + y[i]][c + x[i]];
else
neighbours[i] = Tile::Type::Void;
return neighbours;
}
bool TileManager::inField(int r, int c)
{
if (r < 0 || r >= 25) return false;
if (c < 0 || c >= 25) return false;
return true;
}
tiles[x][y+1], if y is the maximum valid value, will not be NULL except by the grace of . This goes out of bounds and as soon as you go out of bounds all bets are off. You've invoked Undefined Behaviour and pretty much anything can happen. Even what you expected to happen.
The same applies to the reported crash site, tiles[x - 1][y - 1].
Edit: Left out solution. Not helpful.
The only way, short of taking off and nuking the entire site from orbit, is to test the index to make sure it does not puncture the array bounds before using the index on the array. You'll probably want a function to handle this.
void assign_if(Type & neighbour, int x, int y)
{
if(x >= 0 && x < width && y >= 0 && y < height)
neighbour = tiles[x][y];
}
and call it
assign_if(neighbours[0], x, y+1);
and later
assign_if(neighbours[0], x-1, y-1);
Edit: Stealing this from Bob__ for completeness
It is impossible to return a raw array from a function. The array goes out of scope and the pointer to it becomes invalid. Either pass in the array as another parameter or use a std::array or std::vector, both of which can be returned. Thanks to Copy Elision, a smart compiler will likely eliminate the copying costs.
Example:
std::array<Tile::Type, 8> TileManager::GetNeighbours(int x, int y)
{
std::array<Tile::Type, 8> neighbours;
...
return neighbours;
}
Edit by original poster. Here is my solution:
std::array<Tile::Type, 8> TileManager::GetNeighbours(int c, int r)
{
std::array<Tile::Type, 8> neighbours;
const int y[] = { -1, -1, -1, 1, 1, 1, 0, 0 };// 8 shifts to neighbors
const int x[] = { -1, 0, 1, -1, 0, 1, -1, 1 };// used in functions
for (int i = 0; i < 8; ++i)// visit the 8 spaces around it
if (inField(r + y[i], c + x[i]))
neighbours[i] = tiles[r + y[i]][c + x[i]];
else
neighbours[i] = Tile::Type::Void;
return neighbours;
}
bool TileManager::inField(int r, int c)
{
if (r < 0 || r >= 25) return false;
if (c < 0 || c >= 25) return false;
return true;
}
Edit: Caveat
This answer deals directly with solving the problem as asked. See the answer by Kaz for a description of a more practical solution that trades a bit of memory to completely eliminate the need for testing and generating the neighbours array.
The more "practical" way (shorter code that avoids conditional checks) is to create the tile array so that it's it contains an additional "border" of tiles around the valid area. If any tile position is in the valid area, then is valid and so is .
You can have a special type for the border tiles which only they have, and simply include those tiles in the "neighbors" list. If your world has walls, then the border can consist of wall material.
Needless to say, you must never ask for the list of neighbors of a border tile. This is ensured by logic such as not allowing a border tile to be the valid position for anything.
This tile is in the valid area within the border" is a condition that is easier to check, in fewer places, and your program can be structured so that this check is actually just a removable assertion (a check for a situation that should not happen if the program is correct, rather than a check for an expected situation).
In C and C++, we can displace the pointers so that position [0][0] is still the corner of the valid area, yet the out-of-bounds coordinates [-1][-1] are valid indices, as are [w][h].
Firstly, the column array is allocated two elements larger than necessary, and the pointer is the incremented by one. Then the columns are allocated two elements larger, and each pointer is incremented by one before being assigned into the main array.
When freeing the arrays with delete [], you have to remember to decrement each pointer by one.
I am trying to create a segmented tree,
Here is my struct for the node of tree:
struct Node{
int x1, x2; // x coordinates
int y1, y2; // y coordinates
Node * v1;
Node * v2;
Node * v3;
Node * v4;
bool oBo; //check if 1 by 1
bool O;
bool F;
int dimens;
Node(int myx1, int myx2, int myy1, int myy2){
this->x1 = myx1;
this->x2 = myx2;
this->y1 = myy1;
this->y2 = myy2;
this->dimens = abs(x2 - x1);
if (dimens == 1)
{
this->oBo = true;
}
else
this->oBo = false;
this->O = false;
this->F = false;
this->v1 = NULL;
this->v2 = NULL;
this->v3 = NULL;
this->v4 = NULL;
}
};
This is my constructor for the Map
MapTree::MapTree(int iSize)
{
this->size = iSize;
root = new Node(0, size, 0, size);
segment(root);
}
and I am using the this segment function to make sub-segments of the root and then this is function is called recursively on the sub-nodes of root and so on. I get a bad memory alloc on the second segment. i.e when dimens = 2 and I have no idea why this is happening. I tried to fix it by changing the values and size but visual studio is not providing any clear error except bad memory alloc at certain memory location.
here is the segment function:
void MapTree::segment(Node * node)
{
while (node->oBo != true)
{
int dimension = node->dimens;
node->v1 = new Node(0, dimension/2, 0 , dimension/2);
node->v2 = new Node(dimension/ 2, dimension, 0, dimension/ 2);
node->v3 = new Node(0, dimension / 2 , dimension / 2, dimension);
node->v4 = new Node(dimension / 2, dimension, dimension / 2, dimension);
segment(node->v1);
segment(node->v2);
segment(node->v3);
segment(node->v4);
}
and last but not the least the size given for the tree is always the power of 2 so the segments are always going to end up being the size of one by one
Never mind, I figured out what was wrong, I think I did not worded my question here correctly. but after some debugging I found the error, the loop was being called again again from the same position and hence infinite memory allocation. since root node->oBo will never be true hence infinite loop and bad memory alloc.
I'm working with OpenGL at the moment, creating a 'texture cache' which handles loading images and buffering them with OpenGL. In the event an image file can't be loaded it needs to fall back to a default texture which I've hard-coded in the constructor.
What I basically need to do is create a texture of a uniform colour. This is not too difficult, it's just an array of size Pixels * Colour Channels.
I am currently using a std::vector to hold the initial data before I upload it OpenGL. The problem I'm having is that I can't find any information on the best way to initialize a vector with a repeating pattern.
The first way that occurred to me was to use a loop.
std::vector<unsigned char> blue_texture;
for (int iii = 0; iii < width * height; iii++)
{
blue_texture.push_back(0);
blue_texture.push_back(0);
blue_texture.push_back(255);
blue_texture.push_back(255);
}
However, this seems inefficient since the vector will have to resize itself numerous times. Even if I reserve space first and perform the loop it's still not efficient since the contents will be zeroed before the loop which means two writes for each unsigned char.
Currently I'm using the following method:
struct colour {unsigned char r; unsigned char g; unsigned char b; unsigned char a;};
colour blue = {0, 0, 255, 255};
std::vector<colour> texture((width * height), blue);
I then extract the data using:
reinterpret_cast<unsigned char*>(texture.data());
Is there a better way than this? I'm new to C/C++ and I'll be honest, casting pointers scares me.
Your loop solution is the right way to go in my opinion. To make it efficient by removing repeated realloc calls, use blue_texture.reserve(width * height * 4)
The reserve call will increase the allocation, aka capacity to that size without zero-filling it. (Note that the operating system may still zero it, if it pulls the memory from mmap for example.) It does not change the size of the vector, so push_back and friends still work the same way.
You can use reserve to pre-allocate the vector; this will avoid the reallocations. You can also define a small sequence (probably a C style vector:
char const init[] = { 0, 0, 255, 255 };
and loop inserting that into the end of the vector:
for ( int i = 0; i < pixelCount; ++ i ) {
v.insert( v.end(), std::begin( init ), std::end( init ) );
}
this is only marginally more efficient than using the four push_back in the loop, but is more succinct, and perhaps makes it clearer what you're doing, albeit only marginally: the big advantage might be being able to give a name to the initialization sequence (eg something like defaultBackground).
The most efficient way is the way that does least work.
Unfortunately, push_back(), insert() and the like have to maintain the size() of the vector as they work, which are redundant operations when performed in a tight loop.
Therefore the most efficient way is allocate the memory once and then copy data directly into it without maintaining any other variables.
It's done like this:
#include <iostream>
#include <array>
#include <vector>
using colour_fill = std::array<uint8_t, 4>;
using pixel_map = std::vector<uint8_t>;
pixel_map make_colour_texture(size_t width, size_t height, colour_fill colour)
{
// allocate the buffer
std::vector<uint8_t> pixels(width * height * sizeof(colour_fill));
auto current = pixels.data();
auto last = current + pixels.size();
while (current != last) {
current = std::copy(begin(colour), end(colour), current);
}
return pixels;
}
auto main() -> int
{
colour_fill blue { 0, 0, 255, 255 };
auto blue_bits = make_colour_texture(100, 100, blue);
return 0;
}
I would reserve the entire size that you need and then use the insert function to repeatedly add the pattern into the vector.
std::array<unsigned char, 4> pattern{0, 0, 255, 255};
std::vector<unsigned char> blue_texture;
blue_texture.reserve(width * height * 4);
for (int i = 0; i < (width * height); ++i)
{
blue_texture.insert(blue_texture.end(), pattern.begin(), pattern.end());
}
I made this template function which will modify its input container to contain count times what it already contains.
#include <iostream>
#include <vector>
#include <algorithm>
template<typename Container>
void repeat_pattern(Container& data, std::size_t count) {
auto pattern_size = data.size();
if(count == 0 or pattern_size == 0) {
return;
}
data.resize(pattern_size * count);
const auto pbeg = data.begin();
const auto pend = std::next(pbeg, pattern_size);
auto it = std::next(data.begin(), pattern_size);
for(std::size_t k = 1; k < count; ++k) {
std::copy(pbeg, pend, it);
std::advance(it, pattern_size);
}
}
template<typename Container>
void show(const Container& data) {
for(const auto & item : data) {
std::cout << item << " ";
}
std::cout << std::endl;
}
int main() {
std::vector<int> v{1, 2, 3, 4};
repeat_pattern(v, 3);
// should show three repetitions of times 1, 2, 3, 4
show(v);
}
Output (compiled as g++ example.cpp -std=c++14 -Wall -Wextra):
1 2 3 4 1 2 3 4 1 2 3 4
Hey I'm making a generic implementation of an ArrayList in C++ and part of it is implementing MergeSort, but now that I've started testing it I'm running into an odd problem. In my tests I'm using type int for simplicity, and on the second function signature below I'm getting the error:
initializing argument 1 of 'T* ArrayList<T>::mergeSort(T*, int) [with T = int]' [-fpermissive]
Then on the recursive calls in the second function I get this error:
invalid conversion from 'int' to 'int*' [-fpermissive]
I don't understand why it thinks I'm trying to convert from an int to an int...shouldn't both be of type int* !? I'm kind of a noob to C++, and especially generics in C++ (although I'm well versed in Java) so any helpful pointers (no pun intended) are appreciated!
/**
* Runs merge sort on this ArrayList<T>. Interface function to the central,
* recursive, merge sort function.
*/
template<class T>
void ArrayList<T>::mergeSort() {
mergeSort(array, size);
}
/**
* Runs merge sort on the passed in array. Recursive.
*
* #param array the array to sort.
* #param arraySize the size of the array that is to be sorted.
* #return the sorted array.
*/
template<class T>
T* ArrayList<T>::mergeSort(T* array, int arraySize) {
T* returnArray = array;
//If the arraySize isn't 1, recurse. Otherwise return the single element array.
if (arraySize != 1) {
returnArray = new T[arraySize];
//Split arrays further. Recurse.
returnArray = mergeSort(array[0], arraySize / 2);
returnArray += arraySize / 2;
returnArray = mergeSort(array[arraySize / 2], arraySize - (arraySize / 2));
}
return returnArray;
}
In your two recursive calls, you are not passing pointers, you are passing the values in the array at those indices.
if (arraySize != 1) {
returnArray = new T[arraySize];
//Split arrays further. Recurse.
returnArray = mergeSort(array[0], arraySize / 2);
returnArray += arraySize / 2;
returnArray = mergeSort(array[arraySize / 2], arraySize - (arraySize / 2));
}
Change your recursive calls to either:
returnArray = mergeSort(&array[0], arraySize / 2);
returnArray = mergeSort(&array[arraySize / 2], arraySize - (arraySize / 2));
or:
returnArray = mergeSort(array, arraySize / 2);
returnArray = mergeSort(array + (arraySize / 2), arraySize - (arraySize / 2));
I'm not clear on what your first error is, it looks like you've cut off part of the error message.