-4
int main() {

  char** k;

  k = new char*;

  char* k1 = "abc";
  char* k2 = "def";

  *k = k1;
  *(k + 1) = k2;
  delete [] (k + 1);


}

Error: segmentation fault

Could someone explain why I get segmentation fault when freeing (k + 1)? I was able to free k with no problems.

ADD: In the answers it has been said that I can't delete [] (k + 1) since I haven't used new on it; But how do you explain the fact that cout<<*(k + 1)<<endl; printed correctly?

Mark
  • 8,408
  • 15
  • 57
  • 81

4 Answers4

3
k = new char*;

This allocated storage for only a single char*.

*(k + 1) = k2;

This tries to pretend there are two char*s allocated. This may not be the site of the segfault, but it is the error.

delete [] (k + 1);

Here you're trying to delete[] something you did not new[], another error.

EDIT: Deep down, memory is allocated in large chunks, such as pages. So when you allocate a small bit of storage, it's very likely that the memory around it is also valid. It's still very invalid to access it, though.

More to the point, when you say something like new char*, this turns into a call to operator new(sizeof(char*)). Let's say the OS allocates a new 4K page of physical RAM for that at address 0x12340000. The memory manager needs a small structure in there to keep track of the block, something like:

struct mem_block_info {
    void* next_block;
    size_t block_size;
};

So it puts this structure at 0x12340000. Immediately after that, it puts the storage you requested, so (assuming this is a 32-bit machine) it returns a pointer of 0x12340008, since sizeof(void*) == sizeof(size_t) == 4. Then it needs to put a header after your storage to track the unused part of that 4K page, so it doesn't waste memory by allocating another 4K page when you want another char*. That header goes at the address right past the end of your allocated block, 0x1234000C. Once the dust settles, that new char* has put this in memory:

Address    Data
0x12340000 0x00000000
0x12340004 0x00000001
0x12340008 uninitialized; could be anything
0x1234000C 0x00000000
0x12340010 0x00000FF4

The null pointers indicate the end of the allocated and free block linked lists.

So when you do:

*(k + 1) = k2;

k + 1 == 0x1234000C is the next_block pointer for the free block, and you just overwrote it with an invalid value (the address of a string in read-only memory, most likely). This does not immediately cause a segmentation fault, but when the memory manager tries to traverse the free block list, it will wind up looking at that string and misinterpreting it as a block header, then going to the next_block from there which is an invalid address, and boom, segfault.

Mike DeSimone
  • 41,631
  • 10
  • 72
  • 96
  • for *(k + 1) = k2 part I got no error when printing it. It was correct value. Could you elaborate more on why you think it should be an error? – Mark Jul 03 '11 at 12:17
  • @Mark - That is also an error, but you didn't ask about that. :-) You have only allocated 1 pointer with `new char*`. That's `*(k + 0)`. `*(k + 1)` doesn't exist. – Bo Persson Jul 03 '11 at 12:20
  • @Bo Persson How do you explain the fact that it printed fine with cout<<*(k+1)< – Mark Jul 03 '11 at 12:22
  • 1
    @Mark - Writing outside your allocated memory is Undefined Behavior according to the language. Anything could happen - like printing what you expected (or not). – Bo Persson Jul 03 '11 at 12:24
  • @Bo Persson Sorry but I don't buy the "anything could happen" part. I need to know why it print it correctly. – Mark Jul 03 '11 at 12:25
  • 1
    @Mark - Ok, try this then [undefined-unspecified-and-implementation-defined-behavior](http://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior) – Bo Persson Jul 03 '11 at 12:28
2

One can only delete exactly what is returned from new, and same applies to new[] and delete[].

It is Undefined Behavior to pass any address to delete which was not returned by new. Here is the quote from the C++ 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.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • Where did you get this could you provide the link? Thanks. – Mark Jul 03 '11 at 12:15
  • @mark: It is a quote from the C++ standard, it is not circulated freely, One has to pay for it to obtain the soft or hard copy. One may find the drafts [here](http://www.open-std.org/jtc1/sc22/WG21/) though – Alok Save Jul 03 '11 at 12:21
1

k + 1 isn't created using new[]. How can you then delete[] it?

They always come in pairs.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
1

You can delete only the exact pointers returned by new. Everything else is illegal.

Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224