29

I checked the following code in C++ with valgrind with --leak-check=full and it says no memory leak. Why is that?

char *p = new char[256];
delete p;

new[] should be matched by delete[] as far as I know.

Kevin
  • 74,910
  • 12
  • 133
  • 166
Dan Lincan
  • 1,065
  • 2
  • 14
  • 32

6 Answers6

39

Although it's undefined behaviour as @KillianDS says, the difference probably relates to the fact that both delete and delete[] free the underlying memory. The point of delete[] is that the destructors for each object in the array are called before the memory is freed. Since char is a POD and doesn't have a destructor, there isn't any effective difference between the two in this case.

You definitely shouldn't rely on it, however.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
  • 2
    With non-trivial destructor the `delete` will crash, because it stores the count of the elements at the beginning of the array and therefore the pointer is not equal to what the underlying `free` expects and the `free` will raise a fatal assertion. Different compilers and standard C++ libraries do it differently, but AFAIR gcc does this. – Jan Hudec Sep 27 '13 at 11:39
  • 1
    @JanHudec: "will" is way overstating it. The compiler and the generated code can do whatever they please. Including appearing to work. – cHao Sep 27 '13 at 15:36
  • 1
    @cHao: It is *not* overstating (for gcc), because I *looked* at what it generates. See [here](http://ideone.com/lnWBMP) if you don't believe me. (And note, that they can only change this when they increment ABI version, which they didn't for some time.) – Jan Hudec Sep 27 '13 at 15:52
  • @JanHudec: They can change it whenever they want and still have a conforming implementation; what they've promised they will or won't do is another matter entirely, and has nothing to do with C++ (which promises absolutely nothing). – cHao Sep 27 '13 at 16:58
  • @cHao: Yes, they can change it. But when they change it, they must change the ABI version, otherwise they'd break a lot of stuff. – Jan Hudec Sep 29 '13 at 10:38
12

delete and delete[] will be equal only if the p points to basic data types, such as char or int.

If p points to an object array, the result will be different. Try the code below:

class T {
public:
    T() { cout << "constructor" << endl; }
    ~T() { cout << "destructor" << endl; }
};

int main()
{
    const int NUM = 3;
    T* p1 = new T[NUM];

    cout << p1 << endl;
    //  delete[] p1;
    delete p1;

    T* p2 = new T[NUM];
    cout << p2 << endl;
    delete[] p2;
}

By using delete[] all the destructors of T in the array will be invoked. By using delete only p[0]'s destructor will be invoked.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
eleforest
  • 332
  • 2
  • 6
  • 3
    Or it could crash and immolate your PC, it's undefined behavior after all. Depending on the implementation `new[]` could use the first 8 bytes of the allocated buffer to store the number of elements, and then return an offset within the buffer; in this case a `delete` would pass the offseted address to `free`, which could result in `free` not finding the corresponding buffer... – Matthieu M. Sep 27 '13 at 07:38
  • 1
    @MatthieuM.: IIRC when compiling with gcc it _will_ crash (and immolate your PC), because the number of elements is written at the beginning of the array and therefore the pointer is shifted compared to what the underlying `malloc()` returned. It will _not_ crash when compiling with MSVC++, which apparently derives the count from the general block metadata. The difference being that microsoft has one library for C and C++, so the C++ relies on implementation details of the C one while GNU stdlibc++ honours layering and only relies on public interface of the C library. – Jan Hudec Sep 27 '13 at 11:34
11

When I try this, valgrind reports:

==22707== Mismatched free() / delete / delete []
==22707==    at 0x4C2B59C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==22707==    by 0x40066D: main (in /home/andrew/stackoverflow/memtest)
==22707==  Address 0x5a1a040 is 0 bytes inside a block of size 256 alloc'd
==22707==    at 0x4C2C037: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==22707==    by 0x40065D: main (in /home/andrew/stackoverflow/memtest)

It's not really a memory leak, but valgrind does notice the problem.

aschepler
  • 70,891
  • 9
  • 107
  • 161
7

Because it's undefined behavior. In your case, delete may do the work of delete [] in your compiler, but it may not work on another machine.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
1

This is undefined behavior, so we can not reason about its behavior. If we look at the draft C++ standard section 3.7.4.2 Deallocation functions, paragraph 3 says (emphasis mine):

[...] Otherwise, the behavior is undefined if the value supplied to operator delete(void*) in the standard library is not one of the values returned by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, conststd::nothrow_t&) in the standard library, and the behavior is undefined if the value supplied to operator delete[] (void*) in the standard library is not 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.

The actual details are going to be implementation-defined behavior and could vary greatly.

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
0

The difference between delete and delete [] is that the compiler adds code to call the destructor for the deleting objects. Say something like this:

    class A
    {
        int a;
        public:
            ...
            ~A() { cout<<"D'tor"; }
    };

    a=new A[23];
    delete [] a; 

This delete [] a; is transformed to something like,

   for (int i=0; i<23; i++)
   {
       a[i].A::~A();
   }
   std::delete a;

So, for your case since it is an built in datatype there is no destructor to call. So, it transformed as,

   std::delete a;

which intern calls the free() to deallocate the memory. That's the reason that you are not getting any leak. Since memory allocated is completely deallocated using free() in g++.

But best practice is that you have to use delete [] version if you use new [].

Jim Dagg
  • 2,044
  • 22
  • 29
Saravanan
  • 1,270
  • 1
  • 8
  • 15