2

I am learning pointers in C++ and have encountered a very strange problem. Our task is to write our own push_back function for our own class which holds an array of integers.

I have the following simple function:

void IntRow::push_back(int value){
    int *elements_temp = new int[size+1];
    for (int i = 0; i <= size; i++){
        elements_temp[i] = elements[i];
    }
    elements_temp[size + 1] = value;
    elements = new int[size+1];
    for (int i = 0; i <= size+1; i++){
        elements[i] = elements_temp[i];
    }
    delete [] elements_temp;
    size++;
}

This however fails, BUT ONLY SOMETIMES on the line: delete [] elements_temp;

With the following error message:

incorrect checksum for freed object - object was probably modified after being freed.

Why is this happening, and why does it only happen sometimes?

P.S.: Do I actually have to free elements_temp or is it freed anyways after the function exists?

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
Balázs Vincze
  • 3,550
  • 5
  • 29
  • 60

2 Answers2

5

The behaviour of elements_temp[size + 1] = value; is undefined as you are accessing an element outside the array bounds.

This undefined behaviour manifests itself as an occasional program crash.

And yes, you need to call delete[] explicitly yourself on the pointer you get back from new[]. (Out of interest, who is calling delete[] on elements?)

Alternatively, use std::vector<int>.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
1
int *elements_temp = new int[size+1];
for (int i = 0; i <= size; i++){
    elements_temp[i] = elements[i];
}
elements_temp[size + 1] = value;

You allocated elements_temp to store size+1 integers. Valid indexes inside this array go from 0 to size (not size+1), as indexes are 0-based in C++. So, you are writing outside the array bounds in the last line above. This is undefined behavior, and it's likely the cause of your problem.

What you probably want is allocating room for size+1 integers, and copying the previous integers to the new array, then add the last new element:

// Allocate room for size+1 integers:
const int newSize = size + 1; 
int* elements_temp = new int[newSize];

// Copy the old array data to the new array.
// Note: i < size, *not* <= !
for (int i = 0; i < size; i++) {
    elements_temp[i] = elements[i];
}

// Write the new item at the end of the array.
// Note: The index is "size", *not* "size+1"
elements_temp[size] = value;

// Update the size data member in your array class to store the new size
size = newSize;

Moreover, you should delete the old elements array before assigning the newly created one, e.g.:

// Get rid of old array memory
delete[] elements;

// If you don't call delete[] above, you will *leak* the old elements array

// Take ownership of the new array
elements = elements_temp;

A Better Allocation Policy

I'd like to add that, allocating a new array each time you add an item to it is a very inefficient policy. A better approach (used e.g. by the standard std::vector container) is to allocate some memory in advance, creating a kind of available capacity, and then allocate a new array and copying the old data to the new array, only when the old vector has no more room (in other words, only when there's no more "capacity" available).

In fact, dynamic memory allocations with new[] are expensive, and it's better to minimize them. (Although this capacity optimization may or may not be what your teacher wants from you in this learning process.)

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • Thanks, it's fairly clear now! Why are you assigning `const` for the new size? Is it better than just using `size+1`? – Balázs Vincze Oct 19 '17 at 10:32
  • You're welcome. I'm glad I was of some help. I think that defining a read-only variable (i.e. a variable that you cannot modify) with `const` and storing in it the new size value, makes the code clearer and less bug-prone than repeating `size+1` in several places and potentially missing the `+1` somewhere... Anyway, feel free to choose what you find more readable. – Mr.C64 Oct 19 '17 at 10:35
  • Oh, so it is not a question of performance gains right? They perform just the same way? – Balázs Vincze Oct 19 '17 at 10:37
  • Yes, it's something I did for the programmer and the code maintainers, more than the C++ compiler :) I think in this case there's no performance difference. – Mr.C64 Oct 19 '17 at 10:38
  • Haha, nice! Thanks, again! – Balázs Vincze Oct 19 '17 at 10:38