I was working with dynamic memory from a book that I got. As far as I understand every time that we create a new variable we need to delete it, and set the pointer to null, so we don't have dangling pointers .
I created a program that stores values from users in a dynamic Array of [5], and whenever the user adds more I "expand" the array. When expanding, I use a temporary new array that gives me a tough time when I try to delete it. Why does this happen?
size_t arraySize(5), index(0);
int inputvalue(0);
int *ptemporal(nullptr);
int *pvalues = new int[arraySize];
for (;;){
cout << "Enter value or 0 to end: ";
cin >> inputvalue; //enter value
// exit loop if 0
if (!inputvalue) //if 0 break
break;
pvalues[index++] = inputvalue; //store values on pivalores
if (index == arraySize){ //if limit reached create space
cout << "Limit reached. Creating space...";
arraySize += 5; //5 new memory blocks
ptemporal = new int[arraySize]; //temporal value holder.
for (size_t i = 0; i < arraySize - 5; i++) //xfer values to temporal
ptemporal[i] = pvalues[i];
delete[] pvalues; // delete values to
pvalues = ptemporal; // assigning the same addres to pvalues.
**delete[] ptemporal; //causes a problem if I use the delete. if i comment the program works just fine.**
ptemporal = nullptr;
}
}
return 0;
}
**The two asterics are just to show were the problem occurs.
Your issue is that you are deleting ptemporal right after you copy the pointer to pvalues:
pvalues = ptemporal; // assigning the same addres to pvalues.
delete[] ptemporal; //causes a problem if I use the delete. if i commentt the program works just fine.**
In other words, you delete the memory you just created! So the next time you expand the vector, you try to delete it again, resulting in a double free error. This sort of error is where a debugger helps, so you can watch the variable values as your program executes.
// start
ptemporal = nullptr;
pvalues = /* location A */;
// first expansion
ptemporal = /* location B */;
// copy values from location A to B
delete[] pvales; /* location A */
pvalues = ptemporal; /* location B! */
delete[] ptemporal; /* location B */
ptemporal = nullptr;
// second expansion
ptemporal = /* location C */;
// copy values from location B to C, should segfault
// then you delete pvalues (aka location B) again!
// this results in a double free error
delete[] pvales; /* location B */
To fix this, simply remove the line delete[] ptemporal;
You dont need to delete pTemporal. You've deleted pValues and wish to hand over pTemporal into it.
delete [] pValues;
pValues = pTemporal;
pTemporal = NULL;
Related
I've been messing around with dynamic memory and I've hit a huge wall.
I'm trying to create a program where the user enters as many strings as they want, then can quit whenever, however after a second string is entered, the program crashes with out giving me any specific error message.
#include "stdafx.h"
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "new"
int _tmain(int argc, _TCHAR* argv[])
{
//Variables
int i=0,end=0,requiresSize=1;
char ** temp;
char item[256]="a";
char ** requires;
//Initialize each element in requiers
requires = new char * [requiresSize];
for(int j=0;j<requiresSize*2;j++){
requires[j]= new char[256];
}
while(strcmp(item,"q-")){
end=0;
printf("Enter h- for help.\nEnter q- to quit.\n");
printf("Please enter a string\n");
gets_s(item);
if(!strcmp(item,"h-")){
printf("Enter a string to add to the list.\nEnter p- to print the list.\n");
end=1;
}
if(!strcmp(item,"q-")){
break;
}
if(!strcmp(item,"p-")){
if(requires[0]!=NULL){
for(int j=0;j<requiresSize;j++){
printf("%d. %s\n",j,requires[j]);
}
}
end=1;
}
while(end==0){
printf("check1:i=%d\n",i);
//if search index is larger than size of the array,reallocate the array
if(i>= requiresSize){
temp = new char * [requiresSize*2];
//Initialize each element in temp
printf("check2:temp initalized\n");
for(int j=0;j<requiresSize*2;j++){
temp[j]= new char[256];
}
printf("check3:temp itmes initialized\n");
for(int j =0;j<requiresSize;j++){
//for each element in requires, copy that element to temp
temp[j]=requires[j];
}
printf("check4:copied requires into temp\n");
delete * requires;
requires = temp;
printf("check5:deleted requires and set requires equal to temp\n");
delete temp;
requiresSize = requiresSize *2;
}
printf("check6:\n");
//if the index at requires is not empty, check to see if it is the same as given item
if(requires[i]!= NULL){
printf("check8:index at requires is not empty\n");
//I know the error occurs here, something to do with accessing requires[i]
if(!strcmp( item, requires[i])){
printf("check9:index at requires is the same as item\n");
//if they are the same, break out of the loop, item is already included
break;
}else{
printf("check10:index at requires is different\n");
//otherwise, increase the index and check again (continue loop)
i++;
break;
}
}else{
printf("check11:index at requires is null, item added\n");
//if the index is empty, add the item to the list and break out of loop
requires[i]= item;
break;
}
printf("check7\n");
}
}
delete requires;
return 0;
}
Thank you in advance.
You need to realize that an assignment statement such as temp = requires (in this case) is only copying over the pointer, so that temp is now pointing to the same location in memory as requires; it does NOT copy over that memory.
This is leading to two problems:
You are allocating new 256-char arrays to each element of temp, and then reassigning each char* in temp to point to a different location, leaking all of that memory; there is now no way to reference the newly-allocated memory, and thus no way for you to free it.
You are assigning the pointer temp to requires, which, again, just means the two are now pointed to the same location in memory, and then deleting temp, which frees that memory (that, again, requires is also now pointing to).
Also, if you use new[] to allocate an array, you must use delete[] to free it. So requires = new char * [requiresSize]; requires you to use delete [] requires; at the end of the program, instead of just delete requires;. Same for each 256-char element of requires.
So, replace temp[j]=requires[j]; with an appropriate call to strcpy (or strncpy). And do not delete temp; the delete [] requires; at the end will handle that, since it is now pointing to that bit of memory.
Please take a look at this function :
void Remove(char *code){
for (int i = 0; i < _curSize; i++){
if (strcmp(code, products[i]->getCode()) == 0){
delete products[i]; // delete product if found
products[i] = NULL; // reset a pointer
products[i] = new Product(); // create new 'default' product
products[i] = products[_curSize-1]; // copy product at last index at place of deleted product
// delete products[_curSize-1]; // if this command gets exe, program crashes.
products[_curSize-1] = NULL; //
_curSize -= 1;
return;
}
}
So I guess its pretty clear what I try to do. Based on code delete the product from array of product pointers and then place the last product in place of one deleted. I marked where program crashes. If I comment that line it works fine, but isn't that leaked memory? Note that this is for school, so I have to use pointers.
This step seems to have no purpose other than creating a memory leak:
products[i] = new Product(); // create new 'default' product
This step transfers implied ownership of an object:
products[i] = products[_curSize-1]; // copy product at last index at place of deleted product
So this step would not fix a memory leak. It would cause a crash (as you already know it does):
delete products[_curSize-1];
Looking at the whole thing:
void Remove(char *code){
for (int i = 0; i < _curSize; i++){
if (strcmp(code, products[i]->getCode()) == 0){
delete products[i]; // A correct/important step
products[i] = NULL; // Totally useless, but not harmful
// products[i] = new Product(); // Useless and harmful, creates a memory leak
products[i] = products[_curSize-1]; // A correct/important step
// delete products[_curSize-1]; // Very harmful: program crashes.
products[_curSize-1] = NULL; // Maybe useful, anyway not harmful.
_curSize -= 1; // A correct/important step
return;
}
}
I have a pointer to an array of pointers-to-objects, and need to resize the array. I do realize this is a perfect time to use vectors, But I'm not permitted to do so. My code works, but I don't completely follow what I've written, and concerned i may have created memory leaks:
void foo (DataSet &add_data)
{
if (sets == NULL)
{
sets = new DataSet*[1];
sets[0] = &add_data;
}
else
{
DataSet **transfer;
transfer = new DataSet*[num_of_sets];
for (int i = 0; i < num_of_sets; i++) // Copy addresses?
transfer[i] = sets[i];
delete [] sets; // Free the array.
sets = new DataSet*[num_of_sets + 1]; // Create new sets
for (int i = 0; i < num_of_sets; i++) // Copy back
sets[i] = transfer[i];
sets[num_of_sets] = &add_data; // Add the new set
delete [] transfer;
transfer = NULL;
}
num_of_sets++;
}
Why does Visual Studio throw an exception for:
for (int i = 0; i < num_of_sets; i++) // Copy addresses?
*transfer[i] = *sets[i];
but not:
for (int i = 0; i < num_of_sets; i++) // Copy addresses?
transfer[i] = sets[i];
But both code segments compile and run without fault in linux. This code should copy the pointers-to-objects. Is that what is happening with:
for (int i = 0; i < num_of_sets; i++) // Copy addresses?
transfer[i] = sets[i];
And do I need to be concerned if I want to free these objects with say a remove function later?
You do not need to allocate twice, just allocate once the final size:
transfer = new DataSet*[num_of_sets + 1]; // Create new sets - final size
for (int i = 0; i < num_of_sets; i++) // Copy addresses?
transfer[i] = sets[i];
delete [] sets; // Free the array.
sets = transfer;
sets[num_of_sets] = &add_data; // Add the new set
// now no need to delete[] transfer
That way you also get improved exception safety btw. - in your original code, you deleted the sets before allocating the new data to it - if that would throw std::bad_alloc, not only your object will become inconsistent (having a dangling sets ptr because you do not assign null to it after delete) but also the memory allocated to transfer would leak. If you allocate transfer directly to final size before delete[] sets, if that will throw, sets will stay intact and transfer will not leak (because it threw during allocation i.e. did not allocate).
Of course, make sure that you delete[] sets in the destructor (and maybe the pointers as well, in case your set is owning them).
*transfer[i] = *sets[i];
Does not copy addresses, like the other sample (without asterisks) does, it tries to dereference the uninitialized pointer elements of transfer and call operator= on DataSet objects on these addresses.
It's undefined behavior, that's why it appears to work under changed circumstances.
When using Visual Studio 2013 heap corruption is detected on calling free().
It is not detected on Linux.
My code seems to run fine until it hits the free function. I call free many times, but in one specific instance, it causes a window to pop up saying HEAP CORRUPTION DETECTED.
I've allocated memory to store pointers to strings(words) for two sets of words. For the first set of words, upon freeing them, no error message is given.
For the second set of words, upon freeing them, the error message pops up.
Here is the code for the first set of words, I made an array of words called arrayFictionary. The function takes a pointer to the array of pointers and adds on new words. No error message is given upon freeing(arrayFictionary).
void ConvertFictionary(char*** parrayFictionary, char* fictionary) {
char * pTemp = 0;
int32_t count = 1;
// put first fictionary word into an array
if(!(pTemp=strtok(fictionary, "\n"))) {//if the dictionary end is reached,
(*parrayFictionary)[count-1] = pTemp; // exit while loop
goto skipD;
}
*parrayFictionary = (char**) realloc(*parrayFictionary, (count + 1)*sizeof(char*));
(*parrayFictionary)[count-1] = pTemp;
count++;
while(1) {// put fictionary words into an array, exit when done
if(!(pTemp=strtok(NULL, "\n"))) {//if the dictionary end is reached,
(*parrayFictionary)[count-1] = pTemp; // exit while loop
break;
}
*parrayFictionary = (char**) realloc(*parrayFictionary, (count + 1)*sizeof(char*));
(*parrayFictionary)[count-1] = pTemp;
count++;
}
skipD:
return;
}
Here is the code for allocating an array for my second set of words called arrayFarticle. Same method is used with slight differences. Error message results upon calling free(arrayFarticle).
void ConvertFarticle(char*** parrayFarticle, char* farticle)
{
char * pTemp = 0;
int32_t count = 1;
// put first farticle word into an array
if(!(pTemp=strtok(farticle, "0123456789.,;: '\"\n!##$%%^&*()_-+=|\\[]{} <>?/~`’"))) //if the farticle end is reached, exit while loop
{
(*parrayFarticle)[count-1] = pTemp;
goto skipA;
}
if(strlen(pTemp)>=2)
{
*parrayFarticle = (char**) realloc(*parrayFarticle, sizeof(char*)*count + sizeof(char*)*2);
(*parrayFarticle)[count-1] = pTemp;
count++;
}
while(1) // put farticle words into an array, exit when done
{
if(!(pTemp=strtok(NULL, "0123456789.,;: '\"\n!##$%%^&*()_-+=|\\[]{}<>?/~`’"))) //if the farticle end is reached, exit while loop
{
(*parrayFarticle)[count-1] = pTemp;
break;
}
if(strlen(pTemp)>=2)
{
*parrayFarticle = (char**) realloc(*parrayFarticle, sizeof(char*)*count + 1);
(*parrayFarticle)[count-1] = pTemp;
count++;
}
}
skipA:
return;
}
I honestly don't know what's going on. I made sure that arrayFarticle isn't being written past its allocated limit.
Your last call to realloc results in *parrayFarticle pointing to a block with an odd (in the mathematical sense) size, and (quite likely) too small.
I am working on a lab dealing with queues, which I don't think is entirely relevant. My task is to create a "priority queue" and the best way I could think of to do it is as follows
void IntQueue::enqueue(int num,int priorityOfEntry)
{
if (isFull())
cout << "The queue is full.\n";
else
{
// Calculate the new rear position
//insert correct lab code here haha
if (priorityOfEntry == 1)
{
rear = (rear + 1) % queueSize;
queueArray[rear] = num;
queueSize++;
}
else if (priorityOfEntry == 2)
{
queueSize++;
int* newArray = new int[queueSize];
newArray[0] = num;
for(int counter = 0;counter< queueSize; counter++)
{
newArray[counter+1] = queueArray[counter];
}
queueArray = newArray;
delete [] newArray;
}
else cout << "invalid priority" << endl;
// Insert new item
// Update item count
numItems++;
}
}
I only have 2 priority levels, 1 and 2, that I explain in the main program. when they all have equal priority it of course works fine, but when I bump on up in priority it throws an error at my destructor.
I really don't think this is the right way to approach this lab, but It seems to work.. at least if I can actually get this memory error fixed.
I figure the only problem could be in that I change the address of what the destructor thinks it will delete.. but I thought pointers would already kind of account for that.
I understand I need to learn to debug my own programs. I really do. but sometimes I just stare at code and there is nothing but a brick wall there. Guess that's what a nudge in the right direction is for.
queueArray is a dangling pointer after this:
queueArray = newArray; // Both 'queueArray' and 'newArray' point to
// the same memory after this assignment.
delete [] newArray;
as the memory that queueArray is pointing to has been deleted. Any attempt to access or destroy queueArray is accessing memory that has already been destroyed. The correct order is:
delete[] queueArray;
queueArray = newArray;
Additionally, there is a potential out-of-bounds access in the for loop that performs the copying:
for(int counter = 0;counter< queueSize; counter++)
{
// When 'counter == queueSize - 1'
// 'newArray[counter + 1]' is one past the end.
newArray[counter+1] = queueArray[counter];
}
Here:
queueArray = newArray; // queueArray and newArray point to the same place
delete [] newArray; // that place gets delete[]ed
you are making queueArray point to the same place as newArray, but then you are deleting the array that lies in that location. So queueArray is left pointing to memory you have given back to the OS, i.e it is now a dangling pointer.
You need to delete queueArray[] first, then assign newArray to it.
Okay, I got it, I don't know why I thought I needed to add another member of the array when the priority switched, I think i'm just tired.
So that was the extra array member
and i think that was the only other problem