How to Delete[] a Pointer That Points into an Allocated Array, But Not to the Start of It

Can I delete[] a pointer that points into an allocated array, but not to the start of it?

No, it is undefined to pass any address to delete which was not returned by new.

Here is the quote from the Standard.

§ 3.7.4.2-3

If a deallocation function terminates by throwing an exception, the behavior is undefined. The value of the first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call has no effect. Otherwise, the value supplied
to operator delete(void*) in the standard library shall be one of the values returned by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, const std::nothrow_-t&) in the standard library, and the value supplied to operator delete[](void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[](std::size_t) or
operator new[](std::size_t, const std::nothrow_t&) in the standard library.

Is there any way to delete a dynamically allocated array in another function?

My suspicion is that you are confused by the usual imprecision when we say "delete a pointer". More correct would be to say delete[] x deletes the array that x points to.

In main you do

copyPtr = copyArray(a, 8);

Now copyPtr does point to the copy of the array. When you write

delete[] copyPtr;

You delete the copy of the array.

What you miss is to delete the initial array you created via int* copyPtr = new int[8]; and because you lost any pointer to it you cannot delete it anymore. You could keep the pointer to that initial array and delete it as well. Though, there is no point in allocating an array just to throw it away and create a new array inside the function. Change your code to

int main()
{
int a[] = { -2, -3, 4, -1, -2, 1, 5, -3 };
int* copyPtr = copyArray(a, 8);

for (int i = 0; i < 8; ++i)
cout << copyPtr[i] << " ";
delete[] copyPtr;
}

PS:

I understand the use of smart pointers but I cant use it in this case because of the prototype given by my teacher.

Your conclusion is not right. Smartpointers can interoperate with raw pointers quite well. You just need to take care with ownership. int* arr is passed as raw pointer. Thats completely fine, because the function does not participate in ownership of that array. It merely reads its values. Raw pointers are for non-owning pointers. If you want to take ownership of the returned pointer you can use a std::unique_ptr<int[]>:

int main()
{
int a[] = { -2, -3, 4, -1, -2, 1, 5, -3 };
auto copy = std::unique_ptr<int[]>(copyArray(a,8));

for (int i = 0; i < 8; ++i) std::cout << copy[i] << " ";
// no delete !!! the unique_ptr does that for you
}

how to properly delete a pointer to array

The requirement to match new[] with delete[] is technically correct.

Much better, however (at least in my opinion), would be to forget that you ever even heard of new[], and never use it again. I'm pretty sure it's been (at least) 10 years since the last time I used new[], and if I'd understood the situation very well, I'd have stopped even sooner than that. Pretty nearly any time you'd even consider using new[], an std::vector (or possibly std::deque) will be a better choice.

If you're trying to create something roughly equivalent to a vector or deque yourself, you don't normally want to use new[] for that either. They way they (at least normally, though it's possible to change this via a custom Allocator class) is to allocate "raw" storage with operator new (which is pretty much like malloc--you just give it a size, and it gives you that many bytes of storage). Then you use the placement new operator to create objects in that space, and explicitly invoke the object's destructor to destroy objects in that space.

To give one example, this is what allows std::vector to support reserve, which allows you to allocate extra space, which won't be turned into objects until you call something like push_back or emplace_back to create an actual object in the space you allocated.

When you use new[], it has to create objects of the specified type filling all the space you allocate. You can't create something like push_back that adds a new object to the collection, because the collection is always already "full". All you can do is allocate a new collection that's larger than the old one (so every addition to the collection is O(N) instead of the amortized O(1) supported by std::vector--a huge loss of efficiency).

Deleting memory allocated with pointers (dynamic array)

  1. When I did delete [ ] temp; was the memory deallocated

    Yes it was. Using the memory after deletion causes undefined behavior, i.e. anything can happen, including but not limited to: code compiles, code crashes, code runs as expected, …

    if i do a loop to populate elements of temp I will still let me populate it even after I used delete [ ] temp;

    C++ has no mechanism to prevent use-after-delete. If you're doing that then you're simply invoking undefined behavior.

  2. Is it possible to deallocate memory if more than one pointer is pointing to that memory location?

    Yes. Again, there's nothing in C++ that's going to stop the programmer from doing this. You have to make sure your code is not double-deleting anything or accessing anything that has already been deallocated.

  3. I cannot do delete [ ] pointers; because I never allocated memory for pointer I just pointed it to the temp memory address.

    You can delete the memory via pointer too. Any pointer that points to the allocated memory and has the same type that was used to allocate the memory can be used to delete it.

    Finally, do as dasblinkenlight said: avoid advanced manual memory handling like new and delete, and prefer smart pointers instead. They will automagically deallocate your memory 1) at the right time, and 2) only once. After that they reset themselves to nullptr making use-after-delete easy to spot.

Proper way to delete an array of pointers

Every pointer allocated with new gets a corresponding delete. Every pointer allocated with new [] gets a corresponding delete []. That's really all you need to know. Of course, when you have a dynamically allocated array which contains dynamically allocated pointers the deallocation must occur in reverse order.

So it follows that the correct idiom would be...

int main()
{
int **container = new int*[n];
for(int i = 0; i < n; ++i)
container[i] = new int[size];

// ... and to deallocate...
for(int i = 0; i < n; ++i)
delete [] container[i];

delete [] container;
}

And then of course I say "stop doing that" and recommend you use a std::array or std::vector (and the template type would be unique_ptr<int>).

What will happen if we delete an array after increment the pointer?

What will happen

Case 1: Undefined behaviour.

p was not returned by the call to new (but instead by new[]), so delete p; is UB.


Case 2: Undefined behaviour.

p+1 was not returned by the call to new[], so p++;delete[] p; is UB.

Why can't I delete pointer alone from an array of objects?

delete works on pointers (and only pointers to dynamically allocated memory blocks) but you don't have an array of pointers, so delete doesn't work on the array elements, it can only work on the array pointer. And it should be the delete [] operator in particular.

If you want to remove an element from the array, you must iterate the array, and when you reach the element you want to remove, start overwriting the array with the next element. Also, you might want to allocate a new, shorter array, and do that operation there, then delete the old array and set the array pointer to the new array.

Ideally, you should use an std::vector class, which does everything for you automatically:

std::vector<A> v(5); // gives you a vector of 5 A elements
v.erase(v.begin() + 2); // erases the element at index 2

This way is less prone to errors and is preferred, and is just as fast and efficient as the error-prone manual way.

Now if you had an array/vector of pointers to elements, that would be a different matter, then you'd have to make sure you delete the element and erase it from the vector. Naturally, if you use smart pointers, you can simply erase the pointer from the vector and it will be automatically deleted.

why p[n] is not pointer?

The [n] operator for pointers is just a shortcut to deference a pointer with an offset. So p[2] is equivalent to *(p + 2). And when you dereference a pointer, you get a reference to whatever it points to, in your case an A instance. So delete p[2] can only work if it is an array of A pointers, not A instances. Note that in your case &p[2] will actually be a pointer, it will be the same value as p + 2 because the & operator will take the address of the reference, but you should not use delete on it, as it itself doesn't point to dynamcally allocated memory, even if it itself happens to be in such memory.

As you yourself noted in the question p == &p[0] but nonetheless, p != p[0]. This means neither should you delete &p[0] even if both are pointers to the same address. Since the memory for the array has been allocated with new [] it should be deallocated with delete [], and you should not use the delete operator on the array or any of its elements.

&p[0] gives you the address of the first element in the array, which coincides with the beginning of the array, but p == &p[0] is just a value comparison. p may be "equal" to &p[0] but it is not the same semantically, as p is a pointer to A [] and &p[0] is a pointer to A.

How to use delete[] when pointer outside memory allocation?

Copy the pointer before you increment it.

int inFramesToProcess = 4;
float *ptr = new float[inFramesToProcess]();
float *ptr_copy = ptr;

for(UInt32 i = 0; i < inFramesToProcess; ++i) {
ptr_copy++;
}

delete[] ptr;

Or just don't use pointers for dynamic arrays, use a vector instead. Then you don't have to worry about deleting.



Related Topics



Leave a reply



Submit