Question

when you allocate dynamic memory on the heap using a pointer,

char *buffer_heap = new char[15];

it would be represented in memory as:

 ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««þþþ

why doesn't there be a NULL terminating character at the end instead of ýýýý««««««««þþþ?

Was it helpful?

Solution

Í is byte 0xCD, which the Windows debug allocator writes into your 15 bytes of memory to indicate that it is uninitialised heap memory. Uninitialized stack would be 0xCC. The idea is that if you ever read memory and unexpectedly get this value, you can think to yourself, "hmm, I've probably forgotten to initialise this". Also, if you read it as a pointer and dereference it, then Windows will crash your process, whereas if an uninitialised buffer were filled with random or arbitrary values then sometimes by fluke you'd get a valid pointer, and your code might cause all kinds of trouble. C++ doesn't say what values uninitialized memory holds, and non-debug allocators won't waste time filling memory with special values for every allocation, so you must never rely on that value being there.

This is followed by 4 bytes of ý (byte 0xFD), which the Windows debug allocator uses to indicate an out-of-bounds region at the end of a buffer. The idea is that if you ever find yourself in the debugger writing to a region that looks like this, you can think "hmm, I've probably overrun my buffer here". Also, if the value has changed when the buffer is freed, the memory allocator can warn you that your code is wrong.

« is byte 0xAB, and þ is 0xFE. Presumably these are also intended as eye-catchers (they aren't plausible pointers or offsets, so they don't form part of the heap structure). I don't know what they signify, possibly more guard data like the 0xFD.

Finally, I guess, you've found a 0 byte, the 16th byte beyond the end of your 15 byte buffer (i.e. the 31st byte counting from the start of it).

Asking the question as "C++" without mentioning that you're on Windows suggests that this is how C++ behaves. It isn't, it's how one implementation of C++ behaves, with particular compiler options and/or linked dlls. C++ does not permit you to read past the end of the buffer, Microsoft is just being nice to you and letting you get away with it not crashing or worse.

OTHER TIPS

You haven't initialized that memory. You are just seeing whatever was already there...

You need to initialize it. Built-in types can be initialized to zero by explicitly calling the default constructor:

char *b = new char[15]();

While every C style string is represented as a sequence of chars, not every sequence of chars is a string.

The \0 usually comes in when you directly assign a string literal, or when you add it there yourself. And it is only meaningful if you treat that array as a string with functions that take the \0 into account.

If you just allocate the memory and don't initialize it, it's full with random stuff. There may be a 0 there or there may not be - you're going to have to put something meaningful there in a subsequent step. It is up to you whether to make that something a string or not.

Because char is a native type, it's uninitialized. That's just how C++ is (it's a legacy of C).

Just accept that and 0 terminate it yourself:

char *buffer_heap = new char[15];
*buffer_heap = '\0';

or if you want the entire buffer initialized:

std::fill(buffer, buffer + 15, 0);

It'll only be initialized if you allocate a type that's initialized. Otherwise, if you want some meaningful values there, you'll have to write them in yourself.

On the other hand, the better answer is that you just shouldn't do this in the first place. Forget that new[] exists, and don't look back.

In GNU C++ (g++) on Linux, this program exits pretty quickly:

#include <algorithm>
#include <iterator>
#include <vector>
#include <cstddef>
#include <cstdlib>
#include <iostream>

namespace {

class rand_functor {
 public:
   int operator ()() const { return ::std::rand(); }
};

}

int main()
{
   using ::std::cout;
   using ::std::vector;
   using ::std::ostream_iterator;
   using ::std::generate;
   using ::std::equal;
   using ::std::copy;

   char *tmp = new char[1000];
   // This just fills a bunch of memory with random stuff, then deallocates it
   // in the hopes of making a match more likely.
   generate(tmp, tmp+1000, rand_functor());
   delete[] tmp;
   vector<char *> smalls;
   smalls.push_back(new char[15]);
   do {
      smalls.push_back(new char[15]);
   } while (equal(smalls[0], smalls[0]+15, smalls[smalls.size() - 1]));
   cout << "        In one allocation I got: [";
   copy(smalls[0], smalls[0]+15, ostream_iterator<char>(cout));
   cout << "]\nAnd in another allocation I got: [";
   copy(smalls[smalls.size() - 1], smalls[smalls.size() - 1]+15,
        ostream_iterator<char>(cout));
   cout << "]\n";
   cout << "It took " << smalls.size() << " allocations to find a non-matching one.\n";
   return 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top