Output affected by placement of delete statement

Hello,

I'm a computer science student and am working on a homework problem. I have already solved it; however, I noticed some odd behavior based on where I put my delete statement within my code. I've included in the code below, which should run fine, and there is a commented out line for the secondary placement.

The homework problem is to create a secondary array that lists "missing integer" within an array of range 'n'. We are required to use this function in its form exactly int* findMissing(int arr[], int n, int& resArrSize). The homework was basically to write the body of this function. Which means that vectors are also off the table. Other libraries besides cmath, iostream, and string, are not allowed.

When I place the delete statement on this line (line 19) my second modified array will print out as [1,5] sometimes and other times [1,5,6]. It is never consistent.

It is consistently the correct answer when I place it at the end of my main statement.

Given the variance in its behavior, I suspect that the reason for this is over my head...and it's not something I can easily debug. Any insight would be appreciated!

PS - I am truly grateful for this forum. First time posting, but an avid reader :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
using namespace std;
int* findMissing(int arr[], int n, int& resArrSize);
void printArray(int arr[], int arrSize);
int main() {
    //array 1 test
    int array1[] = {3, 1, 3, 0, 6, 4};
    int array1Size = 6;
    int array1resArrSize = 0;
    int* missingArray1;
    missingArray1 = findMissing(array1, array1Size, array1resArrSize);
    cout<<"Array 1 test"<<endl;
    cout<<"before: ";
    printArray(array1, array1Size);
    cout<<endl<<"After: ";
    printArray(missingArray1, array1resArrSize);
    cout<<"\n";
    //when place here the output varies between [1,5,6] and [1,5]
    //delete[] missingArray1;
    //array 2 test
    int array2[] = {0,2,3,4,0,0};
    int array2Size = 6;
    int array2resArrSize = 0;
    int* missingArray2;
    missingArray2 = findMissing(array2, array2Size, array2resArrSize);
    cout<<"Array 1 test"<<endl;
    cout<<"before: ";
    printArray(array2, array2Size);
    cout<<endl<<"After: ";
    printArray(missingArray2, array2resArrSize);
    //cleanup
    //when missingArray1 is placed here, it is consistently [1,5,6]
    delete[] missingArray1;
    delete[] missingArray2;
    return 0;
}
int* findMissing(int arr[], int n, int& resArrSize){
    int* numCount = new int[n];
    int* missNums = new int[n];
    //fill array with zeros
    for(int i = 0; i<n;i++){
        numCount[i] = 0;
    }

    for(int i = 0; i < n; i++){
        int index = 0;
        index = arr[i];
        (numCount[index])++;
    }
    int missNumsindex = 0;
    for(int i = 0; i <= n; i++){
        if(numCount[i] == 0){
            missNums[missNumsindex] = i;
            missNumsindex++;;
        }
        resArrSize = missNumsindex;
    }

    return missNums;
}

void printArray(int arr[], int arrSize){
    cout<<"[ ";
    for(int i = 0; i < arrSize; i++){
        if(i == arrSize - 1){
            cout<<arr[i];
        }else {
            cout << arr[i] << ", ";
        }
    }
    cout<<" ]";
}
Last edited on
The short answer is your arrays are too short.

> for(int i = 0; i <= n; i++)
This is never a good sign when you see <=
A given array[n] has subscripts from 0 to n-1. Saying <= n lands out of bounds.

> int array1[] = {3, 1, 3, 0, 6, 4};
> int array1Size = 6;
In your function, you end up doings array[6] from the list of values in array1, so you need at least 7 elements in your numcount array.



Finally, if you're using Linux, then check out valgrind.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ valgrind ./a.out 
==4711== Memcheck, a memory error detector
==4711== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==4711== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==4711== Command: ./a.out
==4711== 
==4711== Invalid read of size 4
==4711==    at 0x400C50: findMissing(int*, int, int&) (foo.cpp:48)
==4711==    by 0x4009F9: main (foo.cpp:11)
==4711==  Address 0x5ab6c98 is 0 bytes after a block of size 24 alloc'd
==4711==    at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4711==    by 0x400BAC: findMissing(int*, int, int&) (foo.cpp:38)
==4711==    by 0x4009F9: main (foo.cpp:11)
==4711== 
==4711== Invalid write of size 4
==4711==    at 0x400C55: findMissing(int*, int, int&) (foo.cpp:48)
==4711==    by 0x4009F9: main (foo.cpp:11)
==4711==  Address 0x5ab6c98 is 0 bytes after a block of size 24 alloc'd
==4711==    at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4711==    by 0x400BAC: findMissing(int*, int, int&) (foo.cpp:38)
==4711==    by 0x4009F9: main (foo.cpp:11)
==4711== 
==4711== Invalid read of size 4
==4711==    at 0x400C87: findMissing(int*, int, int&) (foo.cpp:52)
==4711==    by 0x4009F9: main (foo.cpp:11)
==4711==  Address 0x5ab6c98 is 0 bytes after a block of size 24 alloc'd
==4711==    at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4711==    by 0x400BAC: findMissing(int*, int, int&) (foo.cpp:38)
==4711==    by 0x4009F9: main (foo.cpp:11)
==4711== 

You can use modern style C++11 with basic STL containers to significantly simplify this code. Note that without bare pointers and mismatch with dynamic array sizes your code will be less error prone.
See
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <vector>
#include <set>

using namespace std;

vector<int> find_missing( const vector<int>& data )
{
    vector<int> result;
    result.reserve(data.size());

    set<int> data_ {data.cbegin(),data.cend()};

    for( size_t i {0} ; i<data.size() ; ++i )
    {
        if( data_.find(i) == data_.end() ) result.emplace_back(i);
    }

    return result;
}

int main()
{
    cout << "Missing numbers for {4,0,1,2,0,1}:" << endl;
    for( const auto& number : find_missing({4,0,1,2,0,1}) )
    {
        cout << number << " ";
    }

    return 0;
}
@tomCPP Thank you--your code is definitely cleaner. For this assignment, we're (technically) allowed to use vectors on this homework assignment, but not set. The teacher is particular about us using topics we haven't covered. This unfortunately includes methods on libraries and certain libraries. They also require that we use this function in its form exactly int* findMissing(int arr[], int n, int& resArrSize) The homework was basically to write the body of this function. Which means that vectors are also off the table.
Last edited on
@salem Thank you! I can see how this would be true, and I updated one of my other statements....what I don't understand is why the answer is affected by the placement of the delete statement. If my arrays are too short, won't they be too short regardless of where my delete statement is placed? To put this another way, if my arrays are too short wouldn't the output always be [1,5,]?

Unfortunately I'm not on linux and don't fully understand the error messages you've shared. My guess is that they are telling me I'm trying to access out of bounds memory...?
Last edited on
going out of bounds of a block of memory can cause any of many unpredictable results. Some code works fine, some code crashes, and sometimes placement of statements change how the memory of the program itself is arranged which in turn causes different unpredictable behavior because the out of bounds memory errors now affect a different piece of memory.

The best thing to take away from this is when you see something like adding a basic cout or other 'harmless' statement cause a change in behavior, you have a memory bug 'somewhere'.

If you want to get way down in the weeds -- new statements store the value for how many bytes of memory was allocates so that delete[] can release them all without the number being provided again. If you go out of bounds and bork up that 'hidden' storage area, you can have all kinds of exciting problems crop up. Typically the hidden area is the start of the memory block. so if you had array x[10]; z = new thing[100]; x[10] = 100; //you may have just written into z's hidden size.
Last edited on
Topic archived. No new replies allowed.