Вопрос динамического распределения памяти - PullRequest
11 голосов
/ 20 февраля 2010

при выделении динамической памяти в куче с помощью указателя,

char *buffer_heap = new char[15];

это будет представлено в памяти как:

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

почему в конце нет завершающего символа NULL вместо ýýýý «« «« «« «þþþ? *

Ответы [ 7 ]

22 голосов
/ 20 февраля 2010

Í - это байт 0xCD, который распределитель отладки Windows записывает в ваши 15 байтов памяти, чтобы указать, что это неинициализированная куча памяти. Неинициализированный стек будет 0xCC. Идея состоит в том, что если вы когда-нибудь прочитаете память и неожиданно получите это значение, вы можете подумать про себя: «Хм, я, вероятно, забыл инициализировать это». Кроме того, если вы читаете его как указатель и разыменовываете его, то Windows приведет к сбою вашего процесса, тогда как если неинициализированный буфер был заполнен случайными или произвольными значениями, то иногда по счастливой случайности вы получаете действительный указатель, и ваш код может вызвать все виды неприятностей. C ++ не сообщает, какие значения хранит неинициализированная память, и не отладочные распределители не будут тратить время на заполнение памяти специальными значениями для каждого выделения, поэтому вы никогда не должны полагаться на то, что это значение находится там.

За этим следуют 4 байта ý (байт 0xFD), который используется распределителем отладки Windows для указания области за пределами допустимого диапазона в конце буфера. Идея в том, что если вы когда-нибудь окажетесь в отладчике, пишущем в регион, который выглядит следующим образом, вы можете подумать: «Хм, я, вероятно, переполнил мой буфер здесь». Кроме того, если значение изменилось при освобождении буфера, распределитель памяти может предупредить вас, что ваш код неверен.

«это байт 0xAB, а þ это 0xFE. Предположительно они также предназначены для привлечения внимания (они не являются правдоподобными указателями или смещениями, поэтому они не являются частью структуры кучи). Я не знаю, что они означают, возможно, больше защитных данных, таких как 0xFD.

Наконец, я думаю, вы нашли 0 байт, 16-й байт после конца вашего 15-байтового буфера (т. Е. 31-й байт, считая с начала).

Задавая вопрос "C ++", не упоминая, что вы работаете в Windows, можно предположить, что именно так ведет себя C ++. Дело не в том, как ведет себя одна реализация C ++, с конкретными параметрами компилятора и / или связанными библиотеками. C ++ не позволяет вам читать за пределами буфера, Microsoft просто любезна с вами и позволяет вам обойтись без сбоев или хуже.

6 голосов
/ 20 февраля 2010

Вы не инициализировали эту память. Вы просто видите, что там уже было ...

4 голосов
/ 20 февраля 2010

Вам нужно инициализировать его. Встроенные типы можно инициализировать нулем, явно вызвав конструктор по умолчанию:

char *b = new char[15]();
1 голос
/ 20 февраля 2010

Он будет инициализирован, только если вы выделите инициализированный тип. В противном случае, если вам нужны какие-то значимые значения, вам придется написать их самим.

С другой стороны, лучший ответ заключается в том, что вы просто не должны делать это в первую очередь. Забудьте, что new[] существует, и не оглядывайтесь назад.

1 голос
/ 20 февраля 2010

Поскольку char является нативным типом, он неинициализирован. Вот так и есть C ++ (это наследие C).

Просто примите это и 0 прекратите это сами:

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

или если вы хотите инициализировать весь буфер:

std::fill(buffer, buffer + 15, 0);
1 голос
/ 20 февраля 2010

Хотя каждая строка в стиле C представлена ​​в виде последовательности символов, не каждая последовательность символов является строкой.

\ 0 обычно появляется, когда вы непосредственно назначаете строковый литерал или когда добавляете его туда самостоятельно. И это имеет смысл, только если вы рассматриваете этот массив как строку с функциями, учитывающими \ 0.

Если вы просто выделяете память и не инициализируете ее, она полна случайных вещей. Там может быть 0, а может и нет - вам придется добавить что-то значимое на следующем шаге. Вам решать, делать ли это что-то как строку или нет.

0 голосов
/ 20 февраля 2010

В GNU C ++ (g ++) в Linux эта программа завершается довольно быстро:

#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;
}
...