Thread: Back To The Basics: Freeing An Array on the Heap?

  1. #1
    Registered User
    Join Date
    May 2005
    Posts
    73

    Back To The Basics: Freeing An Array on the Heap?

    So I've been programming for quite a while and I swear to God when you create an array on the heap.

    Code:
    float *f = new float[5];
    You must, to avoid a memory leak, call:

    Code:
    delete [] f;
    However, if I watch the actual memory the code below appears to clean up all memory just fine does it not?

    Code:
    delete f;
    I was taught this would only delete the pointer and not the entire array, but now that I think about it isn't f's allocation size of 20 bytes just stored in the heap table and when a delete is called it just pulls that # of 20 off the heap table and deletes it?

    First call is just calling malloc(20) and the second is just freeing that 20 bytes no matter which delete call is made? eh? Am I wrong here? What is the difference between delete [] and delete again?

    (If anyone is wondering I'm overriding the new and delete calls to write my own at which point I encountered this seemingly basic issue)
    Last edited by Deo; 04-06-2007 at 04:08 PM.

  2. #2
    Lean Mean Coding Machine KONI's Avatar
    Join Date
    Mar 2007
    Location
    Luxembourg, Europe
    Posts
    444
    I'm not much of an C++ expert but I would say the following:

    This code allocates 5*sizeof(float) bytes and assigns the memory address of the first float to the pointer f:
    Code:
    float *f = new float[5];
    This code frees the 20 bytes you reserved earlier:
    Code:
    delete [] f;
    This code deletes the pointer f, which is the only pointer to the allocated memory. While the memory is still reserved, the pointer f to the memory is gone, which results in a memory leak:
    Code:
    delete f;

  3. #3
    Registered User
    Join Date
    Nov 2006
    Posts
    86
    Quote Originally Posted by KONI View Post
    I'm not much of an C++ expert but I would say the following:

    This code allocates 5*sizeof(float) bytes and assigns the memory address of the first float to the pointer f:
    Code:
    float *f = new float[5];
    This code frees the 20 bytes you reserved earlier:
    Code:
    delete [] f;
    This code deletes the pointer f, which is the only pointer to the allocated memory. While the memory is still reserved, the pointer f to the memory is gone, which results in a memory leak:
    Code:
    delete f;
    i think that u are correct

  4. #4
    The larch
    Join Date
    May 2006
    Posts
    3,573
    You should believe language manuals and not assume that they are lying. For arrays you need delete[] and for single types delete.

    new/delete are very different from malloc/free. They not only allocate raw memory, but they also call the constructors and destructors of each object. So they really need to know what and how many objects you are deleting.

    The number of objects in the array allocated with new[] actually can be stored at (pointer-1), at least it seems so with MingW but it is really illegal to dereference this location yourself. (With the following test program, I indeed have value 5 there.)

    If in doubt, you can always try with a program, that prints what happens if you construct/destruct objects:
    Code:
    #include <iostream>
    
    struct Test
    {
        static int count;
        int id;
        Test(): id(++count)
        {
            std::cout << "Test #" << id << " created\n";
        }
        ~Test()
        {
            std::cout << "Test #" << id << " deleted\n";
            count--;
        }
    };
    
    int Test::count = 0;
    
    int main()
    {
        Test* p = new Test[5];
        delete [] p;
        //delete p;
        //p+=2; delete [] p;    
    }
    The only correct output is with delete [] p;
    Test #1 created
    Test #2 created
    Test #3 created
    Test #4 created
    Test #5 created
    Test #5 deleted
    Test #4 deleted
    Test #3 deleted
    Test #2 deleted
    Test #1 deleted
    delete p;, as the language specification says, deletes just one object:
    Test #1 created
    Test #2 created
    Test #3 created
    Test #4 created
    Test #5 created
    Test #1 deleted
    The last try is of course totally wrong, but quite interesting
    Output with: p+=2; delete []p;
    Test #1 created
    Test #2 created
    Test #3 created
    Test #4 created
    Test #5 created
    Test #4 deleted
    Test #3 deleted
    Note that only 2 objects are deleted. The reason probably is that on this implementation the compiler expects (p-1) to point to the number of objects to be deleted. At this particular point in the memory happens to be the id value of Test #2 - hence two Test objects are deleted.
    This, of couse, is playing with undefined behaviour.

  5. #5
    Registered User
    Join Date
    May 2005
    Posts
    73
    Indeed you are correct the destructors for each object in the array are not being called. So indeed delete without brackets would fail to free all memory.... but what if there is no need for a destructor? That is we never allocated memory inside the class object...

    For example:

    Code:
    struct Test
    {
     int one;
     float two;
    };
    The compiler knows that this structure is exactly 8 bytes in size unless overridden by the user for memory alignment in which case it still knows the exact size... this structure will never increase or decrease in size...

    So:

    Code:
    int main()
    {
      Test *n = new Test[5];
      
      delete n;
    }
    Will simply just call Malloc(40) and Free(40) will it not?

    Step into the new() function... all I'm seeing is a malloc call..

    I think this will result in zero memory leaks even though I'm not calling delete []...


    For example try:

    Code:
    int main()
    {
      float *f = new float[5];
      
      delete f;
    }
    Debug this and look at the memory pointed to by F. If in debug mode you should see:

    Code:
    CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD FD FD FD FD
    That is 20 bytes of uninitialized data followed by 4 bytes for "No Mans Land"

    then after the call to delete with NO brackets I see

    Code:
    DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD
    It would appear there is no memory leaks, am I wrong here? (Visual Studio IDE)

    even the code:

    Code:
    class Test
    {
     int f;
     int p;
     
     Test(){};
     ~Test(){};
    }
    
    int main()
    {
     Test *n = new Test[5];
    }
    The compiler is simply calling Malloc(44)... so why wouldn't one be able to just free that 44 bytes of memory with delete n

  6. #6
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    The behavior is undefined. So it is possible that what looks like it works on your machine now won't work on somebody else's machine, or on your machine later. It may be fun to try to figure what is going on, but you would still never actually want to rely on that behavior in real code.

    Are you using release build? Remember that the compiler does stuff in the debug to help debug, but in the release it might not do the same thing for efficiency reasons.
    Last edited by Daved; 04-06-2007 at 07:07 PM.

  7. #7
    Registered User
    Join Date
    May 2005
    Posts
    73
    The behavior is undefined.
    Whats undefined about it? It Mallocs(40) bytes.. then Frees(40) bytes.

    Are you using release build? Remember that the compiler does stuff in the debug to help debug, but in the release it might not do the same thing for efficiency reasons.
    Yep, having tried release mode yet... but wouldn't it just forgo the debug checks and simply go with release versions of new and delete... which are probably just malloc() and free() release versions.... ? I mean what kind of odd behaviour would occur.. isn't it just sequential memory allocated with a small "heap table" defining the amount of bytes that any given buffer is pointing to? I can't imagine the release doing anything wacky with memory allocation.

    Why risk it and go with non-bracketed deletes?... there isn't... but it is just something I won't be able to live with unless I figure it out.. the behavior I'm seeing with delete is going against all the years of programming I've done thus far.. still doing some research into how memory is allocated and stored, but just figured I'd see if any people had a quick answer..

  8. #8
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    When someone says the behavior is undefined, it means that there is rule according to the C++ standard about what the compiler must do. That means a compiler can do anything it wants. It also means that what the compiler does on one occasion might not be the same as what it does on another occasion (and of course that another compiler/library might do something completely different).

    >> Why risk it and go with non-bracketed deletes?... there isn't... but it is just something I won't be able to live with unless I figure it out..
    The reason you don't risk it is because there is no reason to risk it.

    By the way, in C++ you would rarely do this anyway. Use vector.

  9. #9
    Registered User manofsteel972's Avatar
    Join Date
    Mar 2004
    Posts
    317
    In your example you have not incremented the pointer so it is at pos 0 in the array. Try incrementing the pointer to some arbitrary position and then delete it and see what happens.
    "Knowledge is proud that she knows so much; Wisdom is humble that she knows no more."
    -- Cowper

    Operating Systems=Slackware Linux 9.1,Windows 98/Xp
    Compilers=gcc 3.2.3, Visual C++ 6.0, DevC++(Mingw)

    You may teach a person from now until doom's day, but that person will only know what he learns himself.

    Now I know what doesn't work.

    A problem is understood by solving it, not by pondering it.

    For a bit of humor check out xkcd web comic http://xkcd.com/235/

  10. #10
    Registered User
    Join Date
    Oct 2001
    Posts
    2,934
    Quote Originally Posted by Deo View Post
    even the code:

    Code:
    class Test
    {
     int f;
     int p;
     
     Test(){};
     ~Test(){};
    }
    
    int main()
    {
     Test *n = new Test[5];
    }
    The compiler is simply calling Malloc(44)... so why wouldn't one be able to just free that 44 bytes of memory with delete n
    Obviously if the destructor does something useful, there would be a problem not including the []. But I can also imagine that some compilers may store allocation information differently for an array versus a pointer. In this case not including the [] would result in the allocation information being misinterpreted, even for simple classes or types. Apparently this isn't the case with Visual Studio, but might be with some other compilers.

  11. #11
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > The behavior is undefined.
    You mean unspecified.

    Given the examples, the compiler must destruct 5 objects and delete the memory, but how it 'knows' what the number is is entirely up to the implementation.

    The behaviour is consistent, it's just not written down anywhere as to how it should be done.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  12. #12
    Registered User
    Join Date
    Oct 2001
    Posts
    2,934
    >You mean unspecified.
    I think Daved means the behavior is undefined if you use new to allocate an array, and then use delete without the [] to deallocate the same array.

  13. #13
    The larch
    Join Date
    May 2006
    Posts
    3,573
    The standard is rather clear here. If you allocate an array (new []) and delete a single object (delete p), the result is undefined.
    In
    the first alternative (delete object), the value of the operand of
    delete shall be a pointer to a non-array object created by a new-
    expression, or a pointer to a sub-object (_intro.object_) representing
    a base class of such an object (_class.derived_). If not, the behav-
    ior is undefined.
    In the second alternative (delete array), the value
    of the operand of delete shall be the pointer value which resulted
    from a previous array new-expression.18) If not, the behavior is unde-
    fined.
    Also, there is no reason the delete operation should change the values at this point in the memory. All it means is that memory is not yours any more and you cannot dereference it no longer. (But if you got away with it in release mode, you might see that the array contents are still there unmodified.)
    Last edited by anon; 04-07-2007 at 04:49 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 16
    Last Post: 05-29-2009, 07:25 PM
  2. question about multidimensional arrays
    By richdb in forum C Programming
    Replies: 22
    Last Post: 02-26-2006, 09:51 AM
  3. Array help
    By deedlit in forum C Programming
    Replies: 4
    Last Post: 11-05-2003, 10:55 AM
  4. heap question
    By mackol in forum C Programming
    Replies: 1
    Last Post: 11-30-2002, 05:03 AM
  5. Replies: 3
    Last Post: 04-02-2002, 01:39 PM