Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Динамическое выделение памяти  (Прочитано 30402 раз)
0 Пользователей и 1 Гость смотрят эту тему.
little
Помогающий

de
Offline Offline
Пол: Мужской

« : 16-07-2003 07:00 » 

Возникла у меня такая проблема:
Методу динамически созданного класса передается имя файла, которое он должен обработать. В ответ он выделяет себе память под массив результатов и возвращает ссылку на него.
После этого у меня начало глючить абсолютно все, что связано с выделением памяти, т.е. при попытке выделить определенный объем памяти программа начала плеваться эксепшенами, причем могла выделить, скажем 200 Кб, но при этом не давала 60 Кб. И то, и другое носило обязательный характер - 200Кб выделялись, а меньший или, скажем, больший обьем - ни за что.
Сам класс при этом еще не был убит.
Для выделения памяти использовал new, для удаления delete[]
Пришлось сделать выделение фиксированного обьема памяти в конструкторе класса. Тогда все заработало.
Работаю c VС.net на Атлоне под Вин2К Сервер СП3

В чем проблемма? Подскажите.
Записан
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #1 : 16-07-2003 07:20 » 

Наиболее вероятная причина- ты вылез где -то за пределы выделенной памяти и переписал служебные структуры, которые находятся как сверху так и снизу выделенной из кучи памяти. В результате алгоритм выделения памяти начинает глючить(не может выделить память, попытки выделить память выбрасывают эксепшен из-за обращения по неправильным адресам), так как связи между структурами, описывающими выделенную и свободную память, нарушены.
 Где то в твоей проге глюк, который пишет не туда, куда надо.
Кстати глюк может быть связан не обязательно с работой с выделенной памятью. Был у меня случай, когда неправильно вынимались ресурсы из проги- я считал что вынимаю строку с нулевым символом в конце, а его там не было, в результате работа с этой строкой приводила к тому что затирались служебные структуры кучи, и начинались глюки как у тебя.
Записан
Diletant
Помогающий

de
Offline Offline

« Ответ #2 : 16-07-2003 07:26 » 

Наверное найдутся люди, которые по столь подробному описанию могут что-нибудь посоветовать, но лично я ничего не понял.  Жаль  Нельзя ли привести куски кода с "динамическим созданием класса", описанием метода, и выделением памяти?
Записан
little
Помогающий

de
Offline Offline
Пол: Мужской

« Ответ #3 : 16-07-2003 07:48 » 

Код:
#define c_BuffCoordSize 1000000

class C_M1Data
|
public{
void ConvertTxt2Coords)CString lpszFileName, bool bQuant = false:
|
//...
pBuffCoord = new s_buffcoord[c_BuffCoordSize(;
//...
"
void ConvertCoords2Bin)BYTE **pc_BuffBin:
|
//...
pBuffBin = new BYTE[nEstimatedBuffSize(;
//...
*pc_BuffBin = pBuffBin; //таким образом возвращаю указатель на буфер данных
"

protected{
struct s_buffcoord |
BYTE cmd;
double kf;
int dx1;
int dx2;
";
s_buffcoord *pBuffCoord;
BYTE *pBuffBin;
";


Основная же программа выкидывала Exeption на безобидный
BYTE *pBuff = new BYTE[97000];

Класс создавался следующим образом:
pData = new C_M1Data(init_info.fStepX,init_info.fStepY);
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #4 : 16-07-2003 11:26 » 

Боюсь что проблема может быть не в этом...

Варианты:

1...
class AAA
{

...
char * a; // указатель на массив динамических данных - может быть любого типа.
....

char * fileedit(char * filename); //твоя функция
}


Тело программы:

char * AAA::fileedit(char * filename)
{

a = new char [50000];

......


delete a;
!!! Ошибка - если удаление происходит при удалении переменной именно в самой функции...
Даже запомнив указатель на данные - этого делать нельзя.
return a;
}

Второй вариант

при том же описании ты не делаешь delete каждый раз и выгребаешь память....
Записан

А птичку нашу прошу не обижать!!!
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #5 : 16-07-2003 11:47 » 

Цитата: Гром

char * AAA::fileedit(char * filename)
{

a = new char [50000];

......


delete a;
!!! Ошибка - если удаление происходит при удалении переменной именно в самой функции...
Даже запомнив указатель на данные - этого делать нельзя.
return a;
}


Только при удалении массива не delete a;, надо delete[] a;
Исчерпание памяти из-за неосвобождения я исключаю- для этого надо около гигабайта выделить, чтобы до такого дойти. К тому же признаки проблемы- большие объемы выделяются, а маленькие нет, говорит о повреждении структур, описывющих кучу, то есть при выделении большого объема ф-ция выделения памяти из кучи не дошла до поврежденных структур(наверно потому что не повреждены структуры, описывающие большие блоки), а при выделении маленького сразу попала на поврежденную структуру, то есть повреждены структуры описания маленьких блоков. Я же говорил- кто-то в программе гадит и портит системные структуры.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #6 : 16-07-2003 11:51 » 

Цитата

Только при удалении массива не delete a;, надо delete[] a;

Абсолютно никакой разницы - проверял...
Более того даже в споре с сослуживцем проводили исследование - не имеет значения.

Цитата

Исчерпание памяти из-за неосвобождения я исключаю


Может исчерпываться стек.
Он не резиновый...

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

А птичку нашу прошу не обижать!!!
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #7 : 16-07-2003 12:01 » 

Цитата

Абсолютно никакой разницы - проверял...
Более того даже в споре с сослуживцем проводили исследование - не имеет значения.



Имеет и еще какое. Вполне возможно что в какой то реализации информационные структуры для масиивов и одного элемента совпадают, поэтому такая фенька проходит. Но я не уверен в этом даже для Microsoft, и делать так как ты говоришь не стал бы. На это есть Стандарт(пункт 5.3.4 и 5.3.5), где четко написано что как выделять и освобождать. Игнорируя его ты наживешь много неприятностей.
Кстати удаление массивов приведенным тобой способом также может повредить системные структуры, если они различны для одного элемента и массива.

Цитата


Может исчерпываться стек.
Он не резиновый...



Стек при выделении памяти в куче роли не играет, в приведенном тобой коде затраты стека sizeof(char*)+(затраты на вызов operator new(int)).
При исчерпании стека программа вылетает по эксепшену, так как менеджер памяти не может больше расширить стек- ошибка обращения к памяти не обработана, при таких условиях продолжение работы невозможно.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #8 : 16-07-2003 14:36 » 

Стандарты которые ты привел - не относятся к Майкрософту - повторюсь, я уже просматривал не одну книгу - и везде написано одно и тоже, атавизим ситарого стандарта - но я не спорю, потому как кое- кто (Большой Билл) в принципе плювает на все.

Так что вполне может быть что напиано одно а сделано подругому, но еще ни разу я не писал уничтожение массива как ты , и ни разу небыло проблем.

Насчет стека - когда char то вполне может быть что не страшно, когда структура - тут может быть поразному.
Самое главное, что при динамике и возврате указателя - сама память может быть объявлена пустой но к ней будут обращаться и даже все может сойти - ибо память не пользована никем.
Что будет потом при неоднократной такой вот смене, неизвестно, я это имел ввиду.
Записан

А птичку нашу прошу не обижать!!!
Diletant
Помогающий

de
Offline Offline

« Ответ #9 : 16-07-2003 16:16 » 

Цитата: Гром
Стандарты которые ты привел - не относятся к Майкрософту - повторюсь, я уже просматривал не одну книгу - и везде написано одно и тоже, атавизим ситарого стандарта - но я не спорю, потому как кое- кто (Большой Билл) в принципе плювает на все.



Я бы поверил, если бы сам в свое время не наступал на эти грабли. Жаль С VC6  и даже с  MFC.  Сейчас за давностью лет не вспомнить, но было что-то не слишком сложное, но не совсем тривиальное. То ли массив  double, то ли String. Но вылетало даже в Debug  режиме.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #10 : 16-07-2003 16:25 » 

Хм..интересно - мы что в разных студиях работаем???
Или с разным Microsoft  :?:  Я шокирован!
Записан

А птичку нашу прошу не обижать!!!
grozny
Гость
« Ответ #11 : 17-07-2003 01:13 » 

согласен с Громом: delete[] pArray абсолютно эквивалентен delete pArray

Проверено не раз. Для MSVC6.0 SP5, .NET 2003. Сегодня.

Есть баг в одном из вариантов С/С++ рантайма в исходном MSVC6.0 (без сервис паков!). Если линковать С/С++ рантайм либу типа мультитрэд ДЛЛ релиз (если память не подвела), то да, delete без [] неправильно освобождал массив.
Записан
little
Помогающий

de
Offline Offline
Пол: Мужской

« Ответ #12 : 17-07-2003 05:37 » 

Другой вопрос:
Если я инициализирую указатель NULL-ом - это рпавильно или нет. А то мне кто-то сказал, что NULL - это неправильно, и что для указателей VC использует другое значение.
Записан
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #13 : 17-07-2003 05:44 » 

Тем кто путает delete и delete[].
Ну вы блин даете! Конечно можно забить на Cтандарт и сказать- в такой то библиотеке, такого то производителя можно и так сделать. Но где гарантия что ваш код с этой библиотекой соберут?
 В стандарте строго написано, если вы путаете удаление массива и одного элемента - the behavior is undefined. Вот все ваши программы с перепутанными delete имеют неопределенное поведение.
Записан
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #14 : 17-07-2003 05:46 » 

Цитата: little
Другой вопрос:
Если я инициализирую указатель NULL-ом - это рпавильно или нет. А то мне кто-то сказал, что NULL - это неправильно, и что для указателей VC использует другое значение.


Правильно, если переменная глобальная то для нее вызывается конструктор по умолчанию и он ее NULL инициализирует, если переменная стековая- то сам ее NULL инициализируй.
Записан
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #15 : 17-07-2003 05:50 » 

Цитата: Гром
атавизим ситарого стандарта

 Этот "атавизм" есть в ныне действующем стандарте и всегда будет.
Записан
PSD
Главный специалист

ru
Offline Offline
Пол: Мужской

« Ответ #16 : 17-07-2003 06:52 » 

little, Ты говоришь что без этой про цедуры все работало.
А убивать ее не пробовал, а то может "После добаления но не вследствии"

Не выложишь весь код не только выделение но и все опирации записи по данному указателю.
Записан

Да да нет нет все остальное от лукавого.
little
Помогающий

de
Offline Offline
Пол: Мужской

« Ответ #17 : 17-07-2003 07:21 » 

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

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #18 : 17-07-2003 07:42 » 

little, Делай раз - не возвращайся к такому же коду где все равно не работает.

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

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

А птичку нашу прошу не обижать!!!
NeilPryde
Гость
« Ответ #19 : 17-07-2003 12:31 » 

И три - можно еще обратить внимание на функции _CrtCheckMemory, _CrtDumpMemoryLeaks, _CrtIsValidHeapPointer и иже с ними.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #20 : 17-07-2003 12:46 » 

SlavaI, насчет стандарта я должен хорошенько копнуть это дело - погоди...
Записан

А птичку нашу прошу не обижать!!!
grozny
Гость
« Ответ #21 : 19-07-2003 00:14 » 

из С++ FAQ:
http://www.cs.uu.nl/wais/html/na-dir/C++-faq/part14.html
==============================================================================

[35.7] How do compilers use "over-allocation" to remember the number of
       elements in an allocated array?

Recall that when you delete[] an array, the runtime system magically knows how
many destructors to run[16.13].  This FAQ describes a technique used by some
C++ compilers to do this (the other common technique is to use an associative
array[35.8]).

If the compiler uses the "over-allocation" technique, the code for
p = new Fred[n] looks something like the following.  Note that WORDSIZE is an
imaginary machine-dependent constant that is at least sizeof(size_t), possibly
rounded up for any alignment constraints.  On many machines, this constant will
have a value of 4 or 8.  It is not a real C++ identifier that will be defined
for your compiler.

 // Original code: Fred* p = new Fred[n];
 char* tmp = (char*) operator new[] (WORDSIZE + n * sizeof(Fred));
 Fred* p = (Fred*) (tmp + WORDSIZE);
 *(size_t*)tmp = n;
 size_t i;
 try {
   for (i = 0; i < n; ++i)
     new(p + i) Fred();           // Placement new[11.10]
 } catch (...) {
   while (i-- != 0)
     (p + i)->~Fred();            // Explicit call to the destructor[11.10]
   operator delete[] ((char*)p - WORDSIZE);
   throw;
 }

Then the delete[] p statement becomes:

 // Original code: delete[] p;
 size_t n = * (size_t*) ((char*)p - WORDSIZE);
 while (n-- != 0)
   (p + n)->~Fred();
 operator delete[] ((char*)p - WORDSIZE);

Note that the address passed to operator delete[] is not the same as p.

Compared to the associative array technique[35.8], this technique is faster,
but more sensitive to the problem of programmers saying delete p rather than
delete[] p.  For example, if you make a programming error by saying delete p
where you should have said delete[] p, the address that is passed to
operator delete(void*) is not the address of any valid heap allocation.  This
will probably corrupt the heap.  Bang! You're dead!

==============================================================================

[35.8] How do compilers use an "associative array" to remember the number of
       elements in an allocated array?

Recall that when you delete[] an array, the runtime system magically knows how
many destructors to run[16.13].  This FAQ describes a technique used by some
C++ compilers to do this (the other common technique is to
over-allocate[35.7]).

If the compiler uses the associative array technique, the code for
p = new Fred[n] looks something like this (where arrayLengthAssociation is the
imaginary name of a hidden, global associative array that maps from void* to
"size_t"):

 // Original code: Fred* p = new Fred[n];
 Fred* p = (Fred*) operator new[] (n * sizeof(Fred));
 size_t i;
 try {
   for (i = 0; i < n; ++i)
     new(p + i) Fred();           // Placement new[11.10]
 } catch (...) {
   while (i-- != 0)
     (p + i)->~Fred();            // Explicit call to the destructor[11.10]
   operator delete[] (p);
   throw;
 }
 arrayLengthAssociation.insert(p, n);

Then the delete[] p statement becomes:

 // Original code: delete[] p;
 size_t n = arrayLengthAssociation.lookup(p);
 while (n-- != 0)
   (p + n)->~Fred();
 operator delete[] (p);

Cfront uses this technique (it uses an AVL tree to implement the associative
array).

Compared to the over-allocation technique[35.7], the associative array
technique is slower, but less sensitive to the problem of programmers saying
delete p rather than delete[] p.  For example, if you make a programming error
by saying delete p where you should have said delete[] p, only the first Fred
in the array gets destructed, but the heap may survive (unless you've replaced
operator delete[] with something that doesn't simply call operator delete, or
unless the destructors for the other Fred objects were necessary).

==============================================================================
Т.е. delete p вместо delete[] p есть ошибка. Если деструктор тривиален, и компилятор реализует new[] ассоциативным массивом, то никакой разницы (что и утверждалось) замечено не будет. В противном случае возможна порча памяти.

Ну с практической точки зрения портабельность меня абсолютно не волнует, на С++ от Интел и MSVC delete=delete[]. Код у меня не портируемый на другие ОС по определению - нет нужды.

А теоретически было бы интересно посмотреть как это проявляется на GNU g++ и  на ВСВ.
Записан
grozny
Гость
« Ответ #22 : 19-07-2003 00:29 » 

заглянул в исходники CRT от .NET 2003: все типы оператора new сводятся к Win32 HeapAlloc().
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #23 : 21-07-2003 13:04 » 

В gcc есть разница - в run-time stub-е для этого есть две разные ф-ии:
delete p; // == call __buildin_delete
delete[] p; // == call __buildin_vec_delete
Кстати, и new тоже имеет две ф-ии.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines