I am currently tackling this assignment for my computer science class:
Make your own dynamic array template. It should allow creating contiguous arrays (filled with things of the same type) which you can extend without worrying about running out of space.
Do one version using malloc and free.
Do one version using new and delete.
My version using new and delete works flawlessly; however, in trying to convert my new/delete code to using malloc/free, I keep getting a seg fault. I have narrowed down the segfault (I think), to being in a single function: addData. Take a look at the code in my main I used to test this:
Array2<int> *testArray3 = new Array2<int>(5);
Array2<int> *testArray4;
testArray3->initArray();
testArray3->printArray();
testArray4 = testArray3->addData(7);
testArray4->printArray();
return 0;
This gives a seg fault; however, when I change it to this:
Array2<int> *testArray3 = new Array2<int>(5);
Array2<int> *testArray4;
testArray3->initArray();
testArray3->printArray();
testArray4 = testArray3; //->addData(7);
testArray4->printArray();
return 0;
There is no seg fault. This makes me believe the issue is in my addData function. Here is the code for that:
Array2<T> *addData(T dataToAdd){
Array2 <T> *tmp;
tmp->data = this->getData();
Array2 <T> *newData;
newData->data = (T *) malloc(sizeof(T)*(this->size + 1));
for (int i = 0; i < tmp->getSize() + 1; ++i){
if (i < tmp->getSize()){
//newData->data[i] = tmp->data[i];
newData->setData(tmp->getData()[i], i);
}
else{
//newData->data[i] = dataToAdd;
newData->setData(dataToAdd, i);
}
}
free(tmp->data);
free(this->data);
return newData;
};
I am new to programming as a whole and have not completely wrapped my head around pointers and memory allocation, etc. Any advice you could give me would be greatly appreciated! In case you need to see the rest of the code, here is the entire file I coded my template in. Thank you so much for your time!
#include <iostream>
#include <string>
#include <cstdlib>
#include <sstream>
using namespace std;
template<typename T>
class Array2{
public:
Array2(int size){
this->size = size;
data = (T *) malloc(sizeof(T)*size);
};
Array2<T> *addData(T dataToAdd){
Array2 <T> *tmp;
tmp->data = this->getData();
Array2 <T> *newData;
newData->data = (T *) malloc(sizeof(T)*(this->size + 1));
for (int i = 0; i < tmp->getSize() + 1; ++i){
if (i < tmp->getSize()){
//newData->data[i] = tmp->data[i];
newData->setData(tmp->getData()[i], i);
}
else{
//newData->data[i] = dataToAdd;
newData->setData(dataToAdd, i);
}
}
free(tmp->data);
free(this->data);
return newData;
};
~Array2(){
free(this->data);
};
void initArray(){
for (int i = 0; i < this->size; ++i){
//this->data[i] = i;
this->setData(i, i);
}
};
void printArray(){
//ostringstream oss;
string answer = "";
for (int i = 0; i < this->size; ++i){
//oss << this->data[i] + " ";
cout << this->data[i] << " ";
}
//answer = oss.str();
cout << answer << endl;
};
T* getData(){
return this->data;
}
int getSize(){
return this->size;
}
void setData(T data, int index){
this->getData()[index] = data;
}
private:
int size;
T* data;
};
Array2 <T> *tmp;
Allocates a pointer. This does not point the pointer at anything or allocate any storage for the pointer to point at. What it points at without being explicitly assigned is undefined. If you are lucky, and you are this time, tmp points at an invalid location and the program crashes. If you are unlucky, tmp points at some usable region of program memory and lets you write over it, destroying whatever information was there.
tmp->data = this->getData();
Attempts to access the data member at tmp, but fortunately for you the access is in invalid memory and the program comes to a halt. It also has tmp's data pointing at this's data, and that's a dangerous position to be in. Changes to one will happen to the other because they both use the same storage. Also think about what will happen to this->data if you free tmp->data.
Or perhaps I'm wrong and the halt is here for the same reason:
Array2 <T> *newData;
newData->data = (T *) malloc(sizeof(T)*(this->size + 1));
Both need to be fixed. tmp doesn't have to live long, so we can make it a temporary local variable.
Array2 <T> tmp;
Typically this will be created on the stack and destroyed when the function ends and tmp goes out of scope.
But this will not work because Array2's constructor requires a size so it can allocate the array's storage. You need to find out how big to make it. Probably something along the lines of:
Array2 <T> tmp(this->size + 1);
But frankly I don't think you need tmp at all. You should be able to copy the dataToAdd directly into newData without using tmp as an intermediary.
newData is eventually going to be returned to the caller, so it needs a longer scope. Time to use new.
Array2 <T> *newData = new Array2 <T>(this->size + 1);
And through the magic of the constructor... Wait a sec. Can't use new. That makes this hard. malloc doesn't call constructors, so while malloc will allocate resources for newData, it doesn't do the grunt work to set newData up properly. Rule of thumb is Never malloc An Object. There will be exceptions I'm sure, but you shouldn't be asked for this. I recommend using new here and politely telling the instructor they are on crack if they complain.
Anyway, new Array2 <T>(this->size + 1) will allocate the data storage for you with it's constructor.
There is an easier way to do this next bit
for (int i = 0; i < tmp->getSize() + 1; ++i){
if (i < tmp->getSize()){
//newData->data[i] = tmp->data[i];
newData->setData(tmp->getData()[i], i);
}
else{
//newData->data[i] = dataToAdd;
newData->setData(dataToAdd, i);
}
}
Try:
for (int i = 0; i < tmp->size; ++i){
newData->data[i] = tmp->data[i]; // you were right here
}
newData->data[tmp->size] = dataToAdd;
And back to something I hinted at earlier:
free(tmp->data);
free(this->data);
Both tmp->data and this->data point to the same memory. To be honest I'm not sure what happens if you free the same memory twice, but I doubt it's good. Regardless, I don't think you want to free it. That would leave this in a broken state.
Recap and fixes
Array2<T> *addData(T dataToAdd)
{
Array2 <T> *newData = new Array2 <T>(this->size + 1);
for (int i = 0; i < this->size; ++i)
{
newData->data[i] = this->data[i];
}
newData->data[this->size] = dataToAdd;
return newData;
};
This version leaves this intact and returns a newData that is one bigger than this. What it doesn't do is add anything to this. Which is goofy for a method named addData.
It also leads to stuff like this:
mydata = myData->addData(data);
which leaks memory. The original mydata is lost without deletion, resulting in a memory leak.
What I think you really need is a lot simpler:
Array2<T> & addData(T dataToAdd)
{
this->data = realloc(this->data, this->size + 1);
this->data[this->size] = dataToAdd;
this->size++;
return *this;
};
realloc effectively allocates a new buffer, copies the old buffer into the new one, and frees the old buffer all in one fell swoop. Groovy.
We then add the new element and increment the count of elements stored.
Finally we return a reference to the object so it can be used in a chain.
Usage can be
myData.addData(data);
myData.addData(data).addData(moredata);
myData.addData(data).printArray();
and if you have operator << support written
std::cout << myData.addData(data) << std::endl;
I'd go back over the new version of Array if I were you. Most of the bugs picked off here are conceptual errors and also apply to it. You might just be getting unlucky and it merely looks like it works. I just read C++ Calling Template Function Error. The posted solutions fixed the immediate problem, but did not touch the underlying memory management problems.
As for the rest of your class, I advice following the link and answering What is The Rule of Three? Because Array2 violates the heck out of it.
Related
Recently, I started to learn C++ after I have learned Java, and I was instructed to make a dynamic array, so I tried to make a temp variable which contains what I need and then reassign it into the variable I actually want to use.
void Pile::grow(Stone s){
Stone temp[getLength() + 1];
for (int i = 0; i < sizeof(temp) / sizeof(temp[0]); ++i) {
if (sizeof(temp) / sizeof(temp[0]) < 28){
temp[i] = stoneArr[i];
}
}
stoneArr = temp;
}
But the compiler is giving me an error that I cannot reassign it, for some reason I just can't understand.
void Pile::grow(Stone s)
You are not using s anywhere. Are you supposed to add it to the new array you are trying to create?
Stone temp[getLength() + 1];
This is not legal in standard C++. The size of a fixed array must be known at compile time.
Some compilers support "variable length arrays" as a non-standard extension, but do not rely on them if you need to write portable code. See Why aren't variable-length arrays part of the C++ standard?
To allocate an array dynamically at runtime, use the new[] operator instead, eg:
Stone *temp = new Stone[getLength() + 1];
...
delete[] temp;
Or, use the standard std::vector container instead, eg:
#include <vector>
std::vector<Stone> temp(getLength() + 1);
...
for (int i = 0; i < sizeof(temp) / sizeof(temp[0]); ++i)
You cannot use this sizeof trick on a dynamic array, let alone a VLA. sizeof is evaluated only at compile time, not at runtime.
Since you are copying values from an existing array, use the length of that array instead:
for (int i = 0; i < getLength(); ++i)
if (sizeof(temp) / sizeof(temp[0]) < 28)
Hard-coding a 28 here makes no sense. In fact, this whole if check needs to be removed completely.
stoneArr = temp;
This assignment will not work when temp is a VLA. And stoneArr can't be a VLA anyway.
stoneArr needs to be either a Stone* pointer to a new[]'d array (that is managed by following the Rule of 3/5/0), or a std::vector<Stone> (preferred).
With all of that said, try this instead:
private:
Stone *stoneArr;
int arrLength;
...
Pile::Pile()
: stoneArr(NULL), arrLength(0) {
}
Pile::Pile(const Pile &src)
: stoneArr(new Stone[src.arrLength]), arrLength(src.arrLength) {
for (int i = 0; i < arrLength; ++i) {
stoneArr[i] = src.stoneArr[i];
}
}
Pile::~Pile() {
delete[] StoneArr;
}
Pile& Pile::operator=(const Pile &rhs) {
if (&rhs != this) {
Pile temp(rhs);
std::swap(stoneArr, temp.stoneArr);
std::swap(arrLength, temp.arrLength);
}
return *this;
}
int Pile::getLength() const {
return arrLength;
}
void Pile::grow(const Stone &s){
Stone *temp = new Stone[arrLength + 1];
for (int i = 0; i < arrLength; ++i) {
temp[i] = stoneArr[i];
}
temp[arrLength] = s;
delete[] stoneArr;
stoneArr = temp;
++arrLength;
}
Or:
#include <vector>
private:
std::vector<Stone> stoneArr;
...
// std::vector follows the rule of 3/5/0, so let the
// compiler handle Pile(), Pile(const Pile &), ~Pile(),
// and operator= for you...
int Pile::getLength() const {
return stoneArr.size();
}
void Pile::grow(const Stone &s){
stoneArr.push_back(s);
}
I am practicing memory allocation and disk management with C++. I just all of the work.. it just looks and seem's a little too easy. I am not sure if my pointer and my allocation and deallocations are correct. My Total FreeSpace looks like it will work, but it looks too basic. I just need someone's programming experience. When I try to run this code it gives me some kind of Error.
Bug Error
Please DO NOT ADD any new Global Variable.
const int MMSIZE = 60136;
char MM[MMIZE];
//** Initialize set up any data needed to manage the memory
void initializeMemory(void)
{
//**increments through the POOL_SIZE
for (int a = 0; a < MMSIZE; a++) {
MM[a] = 'NULL';
}
}
// return a pointer inside the memory
// If no chunk can accommodate aSize call onOutOfMemory()
void* allocate(int size)
{
//******NOT SURE*******
int *p = new int;
*p = 5;
return ((void*) 0);
}
// Free up a chunk previously allocated
void deallocate(void* mPointer)
{
//******NOT SURE*******
int *p = new int;
delete p;
p = 0;
p = new int(10);
}
//Scan the memory and return the total free space remaining
int remaining(void)
{
//******NOT SURE*******
int free = 0;
for (int a = 0; a < MMSIZE; a++)
{
if (MM[a] < MMSIZE)
{
free += a;
}
}
int free2 = free - MMSIZE;
return free2;
}
This code looks unfinished for even a sample but
//** Initialize set up any data needed to manage the memory
void initializeMemory(void)
{
//**increments through the POOL_SIZE
for (int a = 0; a < MMSIZE; a++) {
MM[a] = 'NULL';// <=== this should not even compile as the single quote should only take one character like '\x0' or 'N'
}
}
should not even compile as the single quote should only take one character like '\x0' or 'N'
but post the complete module and i can help you more and maybe explain a few things.
Without discussing other aspects of your code (such as memory leaking etc), the specific error you are getting most likely comes from *int_pointer = 0xDEADBEEF; line. int_pointer is equal to 0, because int_pointer = (long *)allocate(sizeof(long)); and your void* allocate(int size) with its return ((void*) 0); always returns 0. So you are getting exactly that exception: attempting to write 0xDEADBEEF at address 0x00000000, which is a forbidden operation (there is some OS specific stuff at low addresses).
I'm trying to write a function that pushes an item onto the end of my dynamically allocated array (not allowed to use vectors). Once it goes to the area to double the size of the list if the list was too small to store the next number, it all goes to hell and starts feeding me back random numbers from the computer. Can anyone see why it's not doubling like it's suuposed to?
int *contents_;
int *temp;
int size_ = 0;
int capacity_ = 1;
void pushBack(int item) /**appends the specified value to DynArray; if the contents array is full,
double the size of the contents array and then append the value **/
{
if (size_ == capacity_)
{
capacity_ = (2*capacity_);
temp = new int[capacity_];
for (int i = 0; i < size_; ++i)
{
temp[i] = contents_[i];
}
delete [] contents_;
contents_ = temp;
}
contents_[size_++] = item;
}
EDIT ** I forgot to mention. This is a function out of a class. This is in the header and in main :
main()
{
DynArray myArray;
myArray.pushBack(2);
myArray.pushBack(3);
myArray.printArray();
return 0;
}
If this is your initial setup:
int *contents_; // Junk
int size_ = 0;
int capacity_ = 1;
Then your code is most likely performing a memory access violation upon the first time it does:
if (size_ == capacity_)
{
// Not entering here, contents_ remains junk
}
contents_[size_++] = item;
As barak implied, the contents_ pointer needs to be initialized. If not, c++ will point it to something you probably don't want it to.
Ok, first of all I've written the method, searched stackoverflow beforehand, and noticed my
idea matched the way most people did it, but, the stack doesn't actually get reversed, but instead weird values are put in it:
I'm doing it like this: I make an auxiliary stack and a while loop with the condition size != 0, and then I call aux.push(pop()) since the pop method also returns the deleted element, so the stack should be reversed, and in O(n) time complexity. But, this happens:
STACK TO BE REVERSED: A C D F -> RESULT: Đ Đ `
I ran a memory leak tester, it told me I had 4 times tried to free up already freed space, so I'm thinking that might be the cause.
More details:
Stack implemented as dynamic array
Here is the code with the relevant functions:
template<typename T>
bool NizStek<T>::push(const T& element){
if(_size == _capacity) increaseCapacity();
if(_size == 0){
_brojE++;
_top++;
_array[_top] = new T(element);
}
else{
_size++;
++_top;
_array[_top] = new T(element);
}
}
POP FUNCTION:
template<typename T>
T NizStek<T>::pop(){
if(_size == 0) throw "Stack is empty";
T oldTop = *_array[_top];
delete _array[_top];
_top--;
_size--;
return oldTop;
}
Reverse function:
template<typename T>
void NizStek<T>::reverse() {
NizStek<T> aux;
while(size() != 0){
aux.push(pop());
}
*this = aux;
}
COPY CONSTRUCTOR(OPERATOR = is the same with the first line being delete[] _array;)
template<typename T>
NizStek<T>::NizStek(const NizStek& rhs){
_size = rhs._size;
_capacity = rhs._capacity;
_niz = new T*[_capacity];
for(int i=0; i<_size ;i++) _array[i] = rhs._array[i];
_top = rhs._top;
}
Thanks in advance!
Since you haven't shown it, I'm guessing you are letting the compiler create your copy constructor, which will do a shallow copy. So this:
template<typename T>
void NizStek<T>::reverse()
{
NizStek<T> aux;
while(size() != 0)
{
aux.push(pop());
}
*this = aux; // Potential problem here!
}
Will set this equal to aux's pointer values. Presumably, your destructor frees up the memory, so when aux goes out of scope, the items pointed to in this (this->_array) are no longer allocated ... so you get junk when you attempt to dereference them.
You can fix that by writing your own copy-constructor and actually doing a deep copy of the data (or use move semantics).
EDIT
With your updated copy constructor, you appear to have another issue:
_niz = new T*[_capacity];
for(int i=0; i<_size ;i++)
_array[i] = rhs._array[i]; // this is still a shallow copy!
_top = rhs._top;
The allocation will create an array of pointers, not an array of objects. So you'll have an array of unassigned pointers (this and aux will be pointing to the same items, so when aux clears them out in its destructor, you are still pointing to junk). I think what you wanted was
_niz = new T[_capacity]; // note the lack of *
for(int i=0; i<_size ;i++)
_array[i] = rhs._array[i];
_top = rhs._top;
Or
_niz = new T*[_capacity];
for(int i=0; i<_size ;i++)
{
_array[i] = new T(*rhs._array[i]); // actually do a deep copy
}
_top = rhs._top;
As a side note, if you are concerned about efficiency, you'll probably want to either use a fixed size array, or use a linked-list. Reallocating and copying the memory buffer every time you push an item that requires a new capacity will be very inefficient for a stack structure.
Here's my code:
template<class T> class Test
{
public:
int Size = 0;
int Length = 0;
T* Items;
Test() {}
~Test()
{
delete [] Items;
}
void Append(const T& newItem)
{
if (Size + 1 >= Length)
{
Length += 250;
T* old = Items;
Items = new T[Length + 250];
for (int i = 0; i < Size; i++)
Items[i] = old[i];
delete [] old;
}
Items[Size] = newItem;
Size++;
}
};
Test<int> test;
for (int i = 0; i < 500000; i++)
test.Append(i);
I'm populating the dynamic array with 500000 integers which must take just 1-2Mb but it takes about 30Mb. There's no problem if i set the initial size to 500000(i.e. no resizing occurring). The grow value(250) seems to affect the memory somehow, if it's larger(for example 1000) then the memory usage is pretty low. What's wrong?
Typically, when you are reallocating an array, you do not want to modify the actual array until the very last second (to maintain exception safety):
T* temp = new T[new_size];
// assume count is the previous size and count < new_size
std::copy(Items, Items + count, temp);
std::swap(temp, Items);
delete [] temp;
Aside from that, there is nothing visible in your code that would cause a memory leak.
The extra size can possibly be due to other optimizations (being turned off) and/or debugging symbols being turned on. What compiler options are you using (and what compiler)? It should be noted that extra size is not necessarily an indication of a memory leak. Have you run this in a debugger or memory profiler which found a leak?
It should also be noted that std::vector does all of this for you.
Looking at your code, you're going to segfault more so than leak memory due to the fact that calling delete or delete[] on a non-NULL, but previously deallocated, pointer is a Bad Thing. Also, I don't believe this is your real code, because what you posted won't compile.
When you delete a pointer, always set it to NULL afterwards. It's good practice to initialize to NULL as well. Let's fix up your code to make sure we don't call delete on previously deallocated pointers. Also, let's initialize our pointer to NULL.
Your misuse of memory probably stems from the following lines of code:
Length += 250;
T* old = Items;
Items = new T[Length + 250];
Notice that you increment Length by 250, but then allocate Length+250 more elements? Let's fix that, too.
template<class T>
class Test
{
public:
int Size;
int Length;
T* Items;
Test() : Size(0), Length(0), Items(NULL){}
~Test() {
if (Items != NULL)
delete [] Items;
}
void Append(const T& newItem)
{
if (Size + 1 >= Length)
{
Length += 250;
T* old = Items;
Items = new T[Length];
for (int i = 0; i < Size; i++)
Items[i] = old[i];
delete [] old;
old = NULL;
}
Items[Size] = newItem;
Size++;
}
};
int main(){
Test<int> test;
for (int i = 0; i < 500000; i++)
test.Append(i);
}