Вопрос о динамическом выделении памяти

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

char *buffer_heap = new char[15];

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

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

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


person cpx    schedule 19.02.2010    source источник
comment
Во-первых, кто сказал, что это вообще строка? Для компилятора вам просто нужно 15 необработанных байтов памяти. Если вам нужна строка, используйте std::string. Так что же это за данные? Это просто то, что случилось там. Большинство компиляторов на самом деле заполняют эти данные данными отладки или другой информацией, поэтому, когда вы используете неинициализированные данные, они, вероятно, имеют непротиворечивый шаблон.   -  person GManNickG    schedule 20.02.2010
comment
Я не знаю, почему люди голосуют против этого, это совершенно правильный вопрос. То, что ОП что-то неправильно понимает, не означает, что мы должны наказать его за это.   -  person GManNickG    schedule 20.02.2010
comment
Связанный: stackoverflow.com/questions/2029651/ stackoverflow.com/questions/ 958549/dynamically-allocated-char Не могу найти точную копию, но клянусь, она есть...   -  person dmckee --- ex-moderator kitten    schedule 20.02.2010
comment
@dmckee: еще один связанный здесь: stackoverflow.com/questions/370195/. Я, вероятно, должен был подумать о сайте Google: stackoverflow.com 0xCD 0xFD, прежде чем я ответил, но все же это не совсем точный обман.   -  person Steve Jessop    schedule 20.02.2010
comment
@GMan: +1, я так рад видеть, что кто-то поддерживает n00bs; нехватка знаний не является грехом, хотя и не удосужилась это узнать и так гордиться этим, ЕСТЬ :)   -  person legends2k    schedule 20.02.2010
comment
@Steve: 0xcd 0xfd, но зависит от компилятора, я никогда не использовал его ...   -  person dmckee --- ex-moderator kitten    schedule 20.02.2010
comment
Да, но Í и ý, являющиеся 0xCD и 0xFD, лишь слегка зависят от реализации. По общему признанию, я видел их раньше, поэтому я должен был знать, что они дадут какие-то результаты...   -  person Steve Jessop    schedule 20.02.2010


Ответы (7)


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

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

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

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

Задавая вопрос как «С++», не упоминая, что вы работаете в Windows, вы предполагаете, что именно так ведет себя С++. Это не так, это то, как ведет себя одна реализация C++ с определенными параметрами компилятора и/или связанными DLL. C++ не позволяет вам читать дальше конца буфера, Microsoft просто любезен с вами и позволяет вам избежать сбоев или чего-то похуже.

person Steve Jessop    schedule 19.02.2010
comment
+1 за подробное описание каждого шестнадцатеричного кода, особенно за рассказ об уловках отладчика; также для объяснения о дисциплине тегов - person legends2k; 20.02.2010
comment
Я добавил тег visual-c++, потому что вы правы, вопрос нуждался в этом. ОП, вероятно, не знал, что это поведение зависит от реализации. - person Omnifarious; 20.02.2010

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

person Community    schedule 19.02.2010

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

char *b = new char[15]();
person Potatoswatter    schedule 19.02.2010

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

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

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

person Uri    schedule 19.02.2010
comment
почему theres всегда ýýýý««««««««þþþ» приходит к концу? - person cpx; 20.02.2010
comment
@Dave17: Там не всегда одни и те же данные. Сделайте цикл, чтобы сделать 100 новых распределений char[15] и посмотрите. Если он всегда одинаков, то это может быть шаблон отладки, используемый вашим компилятором. - person Zan Lynx; 20.02.2010
comment
Я использую VS-2005, я пробовал с 1000 новых символов, и все по-прежнему. - person cpx; 20.02.2010
comment
@Dave: Тогда вы просто видите данные отладки или другую информацию об отслеживании памяти, которая была помещена туда, когда она была освобождена. Это не то, на что можно рассчитывать, это просто мусор. - person GManNickG; 20.02.2010

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

Просто примите это и завершите это самостоятельно:

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

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

std::fill(buffer, buffer + 15, 0);
person R Samuel Klatchko    schedule 19.02.2010

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

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

person Jerry Coffin    schedule 19.02.2010
comment
Только для опытных пользователей: помните, что new[] существует, потратьте некоторое время на выяснение того, как переопределить размещение и массив new и delete, а затем просто используйте вектор в любом случае. - person Steve Jessop; 20.02.2010
comment
@Steve: или, как говорится в старой строке об оптимизации: правило № 1: не делайте этого. Правило №2 (только для продвинутых программистов): не делайте этого сейчас. - person Jerry Coffin; 20.02.2010
comment
Правило №3 (для супер-продвинутых программистов): перестань ковыряться и выпусти чертову штуку ;-) - person Steve Jessop; 20.02.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;
}
person Omnifarious    schedule 20.02.2010