Здравствуйте, DimkaU, Вы писали:
DU>Правильно ли это:
В исходном варианте — нет.
c= new char[len];
DU> delete [] c;
DU>как же delete узнает сколько байт занимает массив?
Размер может храниться дополнительно, либо вычисляться по указателю. Подробности реализации, которых может быть множество.
Алексей Кирдин
Re: Как правильно удалить динамически созданный массив?
От:
Аноним
Дата:
29.11.02 14:34
Оценка:
Здравствуйте, DimkaU, Вы писали:
DU>Правильно ли это:
DU> int len=10; DU> char* c; DU> c= new char(len); DU> delete [] c;
DU>Если память будем выделять в другой процедуре DU>как же delete узнает сколько байт занимает массив?
когда происходит операция new, то возвращается указатель на блок быделенной памяти,
и этом блоке памяти формируются дополнительные байты, в которых хранится размер,
delete оттуда и берет инфу о том, сколько надо освободить памяти,
так что все равно где ты ее выделил
Re: Как правильно удалить динамически созданный массив?
Здравствуйте, DimkaU, Вы писали:
DU>Правильно ли это:
DU> int len=10; DU> char* c; DU> c= new char(len); DU> delete [] c;
DU>Если память будем выделять в другой процедуре DU>как же delete узнает сколько байт занимает массив?
1. Этот код выделит в куче память для одного char и поместит туда char(len).
Нужно: c = new char[len];
2. Удаление блока именно так и нужно делать, а как "delete узнает" это как раз проблема компилятора и зависит от реализации. Например можно выделать больше памяти и в начале хранить размер, т.е. *((int*)(c — sizeof(int))) == размер выделенного блока.
There are 10 types of people in the world, those who don't understand binaries, those who do, and those who understand not only binaries.
Re[2]: Как правильно удалить динамически созданный массив?
Здравствуйте, DimkaU, Вы писали:
DU>Правильно ли это:
DU> int len=10; DU> char* c; DU> c= new char(len); DU> delete [] c;
DU>Если память будем выделять в другой процедуре DU>как же delete узнает сколько байт занимает массив?
Знаю, что new перед выделенным блоком записывает его размер, чтобы delete
знала сколько нужно удалять. Поэтому неважно где выделилась память,
лишь при этом использовался new.
Re[2]: Как правильно удалить динамически созданный массив?
DU>>как же delete узнает сколько байт занимает массив?
Kaa>Размер может храниться дополнительно, либо вычисляться по указателю. Подробности реализации, которых может быть множество.
Ну дак а почему же в вопросе о том, как узнать размер выделенного куска, ответ "никак"?
Раз он знает, сколько выделено (чтобы удалить), почему мне не скажет, когда я у него спрашиваю?
Re[3]: Как правильно удалить динамически созданный массив?
P>Ну дак а почему же в вопросе о том, как узнать размер выделенного куска, ответ "никак"?
P>Раз он знает, сколько выделено (чтобы удалить), почему мне не скажет, когда я у него спрашиваю?
Здравствуйте, Odi$$ey, Вы писали:
DU>>Если память будем выделять в другой процедуре DU>>как же delete узнает сколько байт занимает массив?
O$>компилятор пишет для себя служебную информацию, сколько байт выделено.
Не компилятор, а рантайм
Собственно, это аналогично вызову ptr = malloc(size); ... ; free(ptr);
Информация о размерах заначивается в выделенном блоке, а возвращается указатель со смещением.
(Впрочем, это уже варианты реализации). Важно то, что рантайм может по указателю определить размер выделенной памяти (для malloc(), new, new[] могут быть разные способы!)
Перекуём баги на фичи!
Re[4]: Как правильно удалить динамически созданный массив?
Здравствуйте, Vi2, Вы писали:
Vi2>Вообще, проще посмотреть ответы А.Тарасевича — это как книга!
Это само собой Но всё равно не понял ни фига
Он компилятора требуется только то, чтобы он умел правильно выделять такие массивы при помощи 'new[]' и правильно уничтожать такие массивы при помощи 'delete[]'. Как компилятор это делает и какая дополнительная информация понадобится для этого компилятору и понадобится ли она вообще — личное дело компилятора. Классическим примером такой дополнительной информации является количество сконструированных элементов в массиве, которое нужно, например, для того, чтобы правильно выполнить деструкцию элементов массива. Если компилятору эта информация нужна только для того и только для того, чтобы вызвать правильное количество деструкторов...
Почему же только? Ему же ещё и саму память освободить надо. Бог с ними, в деструкторами. Он захватил, вывел из свободного доступа, потом ему надо отпустить, вернуть в кучу, или как это называется. Он же должен пометить как-то в момент delete, что это место стало пустым А для этого ему нужна длина в байтах. Значит он её всё время знает! Но молчит как партизан. Я на самом деле не очень понимаю, как это всё изнутри устоено, может объяснишь по-простому?
Re[5]: Как правильно удалить динамически созданный массив?
Здравствуйте, Pushkin, Вы писали:
P>Почему же только? Ему же ещё и саму память освободить надо. Бог с ними, в деструкторами. Он захватил, вывел из свободного доступа, потом ему надо отпустить, вернуть в кучу, или как это называется. Он же должен пометить как-то в момент delete, что это место стало пустым А для этого ему нужна длина в байтах. Значит он её всё время знает! Но молчит как партизан. Я на самом деле не очень понимаю, как это всё изнутри устоено, может объяснишь по-простому?
Структура данных new MyObject[N] выглядит приблизительно так:
выделен результат результат
блок malloc() new[]
памяти
blk + 0 : mlc - 2 : arr - 16 : размер выделенного блока : N*sizeof(MyObject) + sizeof(MALLOC,NEW,DEBUG,PAD)
: mlc + 0 : arr - 12 : количество элементов : N
: : arr - 8 : размер элемента : sizeof(MyObject)
: : arr - 4 : адрес деструктора : MyObject::~MyObject
: : arr + 0 : начало массива
.....
То есть new[] выделяет память (скажем, с помощью malloc() — получая указатель mlc) и записывает служебную информацию; вызывает конструкторы всех элементов; а потом отдает адрес начала массива в этом блоке.
delete[] по адресу массива находит начало служебной записи и отрабатывает ее.
Эксперименты показывают, что free(new char[N]) и delete[](char*)malloc(N) работают корректно (как правило). Это говорит о том, что для разных типов данных формат служебной записи разный; а также о терпимости функции free() к указателям не на начало.
Например, блоки могут выравниваться так, что blk всегда делится на 0x40. Тогда достаточно у указателя маскировать 0x3F, чтобы получить адрес начала блока.
Все сказанное выше не обязательно справедливо: как компилятор в паре с рантаймом, так и программист могут изменять операторы new/delete (и функции malloc/free). Пример тому — отладочные средства MFC.
Например, можно вести таблицу выделенных блоков, и по указателю начала массива находить служебную информацию в другом месте.
Перекуём баги на фичи!
Re[5]: Как правильно удалить динамически созданный массив?
Здравствуйте, Pushkin, Вы писали:
P>Это само собой Но всё равно не понял ни фига
В дополнение к предыдущему ответу. Для POD типов (plain old data) конструкции detele и delete[] ни чем не отличаются. Оператор delete[] нужен только для того, чтобы вызвать деструкторы (в виде функций), если они реально существуют. Хороший стиль, однако, всегда предполагает использование delete для удаления одиночного объекта и delete[] для массива. То есть, даже для a = new char[1] надо использовать delete [] a. Ну в конце концов, программист должен знать, что он на самом деле удаляет — объект или массив объектов!
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[6]: Как правильно удалить динамически созданный массив?
Здравствуйте, Кодт, Вы писали:
К>Структура данных new MyObject[N] выглядит приблизительно так:
...skiped...
Нет, ну я примерно так себе это и представлял.
Давай на простейшем разберём — пусть это будет malloc.
Я так понял по твоей схеме, что если она вернула указатель,
то 2 байта перед ним это длина выделенной памяти, так?
1) Почему только 2 байта? надо бы хотя бы 4.
Я могу объяснить это только тем, что память выделяется страницами,
поэтому надо хранить количество страниц, а не байтов.
2) Почему не дать мне стандартную фунцию, возвращающую эти 2 байта?
Почему я должен хакерить?
Фиг с ним, пусть это будет число страниц, а не байт.
Хотя, конечно, лучше бы именно байт — смешно экономить 2 байта в размере
К>Все сказанное выше не обязательно справедливо: как компилятор в паре с рантаймом, так и программист могут изменять операторы new/delete (и функции malloc/free). Пример тому — отладочные средства MFC.
Да, вот это малость убеждает...
Хотя всё равно неясно, почему было не задать жёстко, что 4 байта
перед любым выделенным динамически блоком это его длина в байтах.
О! так я ведь могу malloc переопределить чтоб так и было, правда?
Да и классец можно сделать — CBuffer какой-нить.
Плохо только, что понимаешь это поздно, когда уже привык таскать за собой размер — и в переменных класса и в каждую фунццию проталкивать. Да и стандартных функций полно таких.
Re[7]: Как правильно удалить динамически созданный массив?
От:
Аноним
Дата:
02.12.02 06:52
Оценка:
P>2) Почему не дать мне стандартную фунцию, возвращающую эти 2 байта?
Потому, что нету такого в стандартах. Каждый реализует как захочет.
P>Почему я должен хакерить?
Этого делать не следует (конечно имхо). Всегда может успешно прекратить работать.
P>Плохо только, что понимаешь это поздно, когда уже привык таскать P>за собой размер — и в переменных класса и в каждую фунццию проталкивать.
Почему бы не использовать в собственных функциях различные контейнеры
и различные классы массивов со всякими "умными" наворотами, пусть даже
и свои?
Re[7]: Как правильно удалить динамически созданный массив?
Здравствуйте, Pushkin, Вы писали:
P>Здравствуйте, Кодт, Вы писали:
P>1) Почему только 2 байта? надо бы хотя бы 4.
Это моя очепятка. Если посмотришь на mlc — arr -- там как раз 4 байта.
P>Я могу объяснить это только тем, что память выделяется страницами, P>поэтому надо хранить количество страниц, а не байтов.
Ну, щаз! Если я прошу malloc(1), то мне выделят сразу 16К?
P>Почему я должен хакерить?
Для апишных куч (WinAPI: GlobalAlloc, LocalAlloc, HeapAlloc); (COM: interface IMalloc) есть соответствующие функции (GlobalSize, LocalSize, HeapSize, IMalloc::GetSize).
MS для C-RunTime сделал
#include <malloc.h>
size_t _msize(void*); // это не входит в стандарт ANSI C!
К>>Все сказанное выше не обязательно справедливо: как компилятор в паре с рантаймом, так и программист могут изменять операторы new/delete (и функции malloc/free). Пример тому — отладочные средства MFC.
P>Да, вот это малость убеждает... P>Хотя всё равно неясно, почему было не задать жёстко, что 4 байта P>перед любым выделенным динамически блоком это его длина в байтах. P>О! так я ведь могу malloc переопределить чтоб так и было, правда? P>Да и классец можно сделать — CBuffer какой-нить.
Ага! И реализовать его через malloc() ?
P>Плохо только, что понимаешь это поздно, когда уже привык таскать за собой размер — и в переменных класса и в каждую фунццию проталкивать. Да и стандартных функций полно таких.
Для COM есть IMalloc.
Перекуём баги на фичи!
Re[8]: Как правильно удалить динамически созданный массив?
#include <malloc.h>
size_t _msize(void*); // это не входит в стандарт ANSI C!
Отец родной! А Микрософт мама дорогая
Я знал, что кто-то должен обо мне позаботиться.
Я правда не знал такую замечательную функцию
Вряд ли брошусь её юзать, но хоть спать спокойно буду.
Re: Как правильно удалить динамически созданный массив?
DU>Правильно ли это:
DU> int len=10; DU> char* c; DU> c= new char(len); DU> delete [] c;
Если конкретно этот пример, то правильно, а если :
int len=10;
char* c,d[3]="12\0";
c= new char(len); // если сделать ZeroMemory, то будет ОК
strcpy(c,&d[0]);
....
манипуляции с буфером
....
delete [] c; // даст ошибку ~ DAMAGE after (before)
// normal block
В общем, выделяя память для массива char, ее надо очистить (желательно), и выделять с запасом (+1), тогда не важно где и когда выделил память, delete [] отработает нормально.
Re[2]: Как правильно удалить динамически созданный массив?
Здравствуйте, zerk, Вы писали:
Z>Если конкретно этот пример, то правильно, а если :
Z> int len=10;
Z> char *c, d[3]="12\0";
Z> c= new char(len); // если сделать ZeroMemory, то будет ОК
Z> strcpy(c,&d[0]);
Z>....
Z>манипуляции с буфером
Z>....
Z> delete [] c; // даст ошибку ~ DAMAGE after (before)
Z> // normal block
Z>В общем, выделяя память для массива char, ее надо очистить (желательно), и выделять с запасом (+1), тогда не важно где и когда выделил память, delete [] отработает нормально.
Блин, не мудрено!!!
new char( initial_value_of_the_char )
new char[ size_of_char_array ]
Понятно, в чем разница?
А ошибку дает по очень простой причине: в конце блока есть некоторая "зона стабилизации", в дебаге заполненная, кажется, 0xE5. Если она изменилась — это признак "стрельбы по памяти".
O$>>компилятор пишет для себя служебную информацию, сколько байт выделено.
DU>память выделит программа во время выполнения. DU>компилятор уже будет в прошлом и ничего не знает.
все так, но код, который сгенерил компилятор для выделения памяти, в нужное место положит количество выделенных байтов, а потом delete полезет в это место и посмотрит, сколько там чего :))
Обычно "нужное место" лежит по фиксированному адресу относительно начала выделенной области.