Reputation: 21
int* generateArray(int size)
This function should dynamically create an array and should return the created array to the array generated in the main program.
int main()
{
int *numList = generateArray(501);
cout << "Mode = " << findMode(arr, 501) << endl;
cout << "Median = " << findMedian(arr, 501);
delete[] numList;
numList = nullptr;
return 0;
}
I also need to delete the dynamically allocated array. I wanna make sure if I deleted the new pointer properly. By deleting the generated in the at the end of int main would it delete the new pointer in the function as well?
int *generateArray(int size)
{
srand(time(0));
int *arr = new int[size];
for (int i=0; i<size; i++)
{
arr[i] = rand() % 91 + 10;
}
return arr;
}
Upvotes: 2
Views: 2536
Reputation: 664
I would do it using a static variable inside of your generate function that returns the pointer to the array. Then, inside your delete function, simply call the generate function to give you the pointer and delete it. To delete an array created on the HEAP, use the delete[]
operator. This operator should be called to free memory allocated with new Type[]
, where Type
is any type (e.g. an int
). After you delete (aka deallocate) this memory, it's almost always a good idea to set the pointer that was pointing to the start of the array to nullptr
. That way, you don't accidentally use that pointer again.
Here's some code to show what I mean:
int* getArray(unsigned long long elements_num = 0)
{
static int* arr = nullptr;
if (arr == nullptr && elements_num > 0)
{
arr = new int[elements_num];
std::cout
<< "Address of array being created: " << arr
<< std::endl;
}
return arr;
}
void deleteArray()
{
int* arrayAddress = getArray();
if (arrayAddress == nullptr)
{
std::cerr << "Array not yet created" << std::endl;
}
else
{
std::cout
<< "Address of array being deleted: " << arrayAddress
<< std::endl;
delete[] arrayAddress;
arrayAddress = nullptr;
}
}
int main()
{
constexpr unsigned long long ARR_SIZE = 5;
std::cout
<< "Trying to delete before creating array..."
<< std::endl;
deleteArray();
std::cout << '\n'
<< "Creating array with 5 elements..."
<< std::endl;
int* myArray = getArray(ARR_SIZE);
std::cout << '\n'
<< "Setting the values of the elements..."
<< std::endl;
for (unsigned long long i = 0; i < ARR_SIZE; i++)
{
myArray[i] = static_cast<int>(i) + 1;
}
std::cout << "Values: ";
for (unsigned long long i = 0; i < ARR_SIZE; i++)
{
std::cout << myArray[i] << ", ";
}
std::cout << "\n\n"
<< "Deleting array..."
<< std::endl;
deleteArray();
deleteArray(); // Trying to delete twice... Our program should be fine, right?
}
Here's the output I got from running it:
Trying to delete before creating array...
Array not yet created
Creating array with 5 elements...
Address of array being created: 01147438
Setting the values of the elements...
Values: 1, 2, 3, 4, 5,
Deleting array...
Address of array being deleted: 01147438
Address of array being deleted: 01147438
[Program crashes]
Edit 1
However, don't be so quick to accept this unconditionally. There is still a big issue. If you call deleteArray()
again, it will try to delete the memory again. Why is this? Didn't we set the array back to nullptr
? Well... yes and no.
To understand, think about what a pointer is; it's just a variable. And, just like any other variable type, a pointer takes memory. It's like having a book that takes up a big spot on your desk, but then having a note on the fridge telling you where your book is. The note, in this case, is a pointer, and the book is a non-pointer variable.
Now, imagine you had another note in the bathroom telling you where the note on the fridge is. This second note is like a double pointer. A double pointer simply stores the memory location of the normal pointer.
Back to the problem. What we did when we called getArray
is we created a second pointer variable (that has it's own memory address). So, when we set that second pointer to nullptr
, that doesn't mean we set the original pointer to nullptr
too. It would be like having the note on your fridge plus a note in your living room both telling you where the book is. If you erase what's on the note in the living room, that doesn't mean you also erased what's on the note on the fridge.
So how do we fix this? We have to use a double pointer. Using a double pointer means, when we dereference that pointer one time, we get the actual memory address of the original array.
The thing is, messing with double pointers all the time is rather horrible to look at, so we can make a function that makes things a little more bearable and only use the double pointer when we need to set the original array to nullptr
.
Here's our new code with double pointers:
// Notice the change in return type here
int** getArrayAddress(unsigned long long elements_num = 0)
{
static int* arr = nullptr;
if (arr == nullptr && elements_num > 0)
{
arr = new int[elements_num];
std::cout
<< "Address of array being created: " << arr
<< std::endl;
}
// Notice the return went from "arr" to "&arr"
return &arr;
}
void deleteArray()
{
// Notice the change in variable type here
int** arrayAddress = getArrayAddress();
if (*arrayAddress == nullptr)
{
std::cerr << "Array not yet created" << std::endl;
}
else
{
std::cout
<< "Address of array being deleted: " << *arrayAddress
<< std::endl;
// Notice we have to dereference once before deleting
delete[] *arrayAddress;
*arrayAddress = nullptr;
}
}
// This is our convenience function so we don't have to mess with
// double pointers all the time
int* getArray(unsigned long long elements_num = 0)
{
return *getArrayAddress(elements_num);
}
int main()
{
constexpr unsigned long long ARR_SIZE = 5;
std::cout
<< "Trying to delete before creating array..."
<< std::endl;
deleteArray();
std::cout << '\n'
<< "Creating array with 5 elements..."
<< std::endl;
int* myArray = getArray(ARR_SIZE);
std::cout << '\n'
<< "Setting the values of the elements..."
<< std::endl;
for (unsigned long long i = 0; i < ARR_SIZE; i++)
{
myArray[i] = static_cast<int>(i) + 1;
}
std::cout << "Values: ";
for (unsigned long long i = 0; i < ARR_SIZE; i++)
{
std::cout << myArray[i] << ", ";
}
std::cout << "\n\n"
<< "Deleting array..."
<< std::endl;
deleteArray();
deleteArray(); // Now the program really can handle this
}
And here's its output:
Trying to delete before creating array...
Array not yet created
Creating array with 5 elements...
Address of array being created: 00C573A0
Setting the values of the elements...
Values: 1, 2, 3, 4, 5,
Deleting array...
Address of array being deleted: 00C573A0
Array not yet created
Upvotes: 0
Reputation: 238311
How do I delete a dynamically allocated array that is initialized in another function?
Ideally, you return a RAII container that owns the array, and takes care of the destruction of the array in their own destructor. Such as std::vector
or std::unique_ptr<T[]>
.
In case RAII is not an option, such as a cross language API, when allocation is necessary it is a convention to provide named functions for both creation and destruction of the resource:
int* generateArray(int size); // maybe calls new[]
void destroyArray(int*); // maybe calls delete[]
This allows the user of the API to not depend on the allocation details.
By deleting the generated in the at the end of int main would it delete the new pointer in the function as well?
The function has already returned by that point. All local variables of that function have been destroyed. The "pointer in the function" no longer exists. The pointer in main function is a copy of that pointer: It has the same value.
Deleting one pointer destroys the pointed object (or array), and deallocates the memory. If there were any other pointers or references to that object (or array), those other pointers would become invalid. Those other pointers don't need to be deleted and in fact, attempting to do so would result in undefined behaviour.
Upvotes: 1