Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Memory leaks


Автор: brb 11.1.2003, 00:35
Я старый(и по возрасту тоже  :huh ) и закоренелий Сишник. С памятью работал много и безпроблемно. На билдере реализовал такой код:
  PayState    *ptr;   // класс поинтер вестимо
  PayState    *pPayState[1200];
 
  for (i = 0; pPayState[i] != NULL; i++ )delete pPayState[i];
  // я думал, что тут я освобождаю всю занятую память
  i = 0;
  do {
     ptr = new PayState ( bc, ic ); // конструктор - аллокирует новую запись
     if ( ptr != NULL ){            // успешно
        bc = ptr->Balance();        // какой-то дурацкий код...
        ic = ptr->Bank();
        pPayState[i++] = ptr;       //Поместить в массив для дальнейших нужд  
     }
     else {
        ShowMessage ("Not enough memory, Sorry");
        exit ( EXIT_FAILURE );
     }
  } while ( bc );                   // больше записей нет!

Хоть и С++, но все так, как и должно быть в С. Итог - не работает
Тот же пример но в С++ (говорю честно, срисовал без понимания)

  delete[] pPayState;               //  ЧТО ЖЕ ЭТО ЗА ТАКОЕ???????
  i = 0;
  do {
     ptr = new PayState ( bc, ic );
     if ( ptr != NULL ){
        bc = ptr->Balance();
        ic = ptr->Bank();
        pPayState[i++] = ptr;
     }
     else {
        ShowMessage ("Not enough memory, Sorry");
        exit ( EXIT_FAILURE );
     }
  } while ( bc );

Так - нет проблем! Люди грамотные, объясните в чем дело? Я и синтаксиса такого не знаю: delete[] pPayState;

Заранее благодарен

Автор: Baa 11.1.2003, 01:38
>Я и синтаксиса такого не знаю: delete[] pPayState;
char* lpBuffer = new char [0xFF];
delete [] lpBuffer;
delete [] lpBuffer освобождает ВСЮ память, выделенную нами ранее под МАССИВ символов.


Автор: brb 11.1.2003, 04:00
>char* lpBuffer = new char [0xFF];
>delete [] lpBuffer;
>delete [] lpBuffer освобождает ВСЮ память, выделенную нами ранее под МАССИВ >символов.
А в чем же разница между
delete [] lpBuffer;
и
delete lpBuffer;
И то и другое освобождоют выделенный фрагмент памяти или надо как в Си
free lpBuffer;
a delete lpBuffer;
не проходит?

Автор: skaa 11.1.2003, 06:45
delete [] lpBuffer удаляет все элементы массива lpBuffer, а delete lpBuffer освобождает распределённую память, указатель на которую lpBuffer.

Автор: brb 11.1.2003, 09:20
skaa, замечательное наблюдение!
А как освободить незанятую память? Освободить от чего?
Уважаемый, верните мне, пожалуйста, должок, который Вы у меня не брали! :thumbs-up

Автор: Vaulter 11.1.2003, 19:33
что у тебя в деструкторе PayState?

Автор: brb 12.1.2003, 03:10
Что Бог то бишь Борланд послал. А мысль в чем?

Автор: Fantasist 12.1.2003, 03:41
Не, какая-то ерунда. Надо бы узнать, что значит "не работает". Вот у тебя объявление:

PayState    *pPayState[1200];

Это есть 1200 указателей на PayState, причем выделенных на стеке (или в сегменте данных, если она глобальная, но это детали). Применять к этой переменной delete[] ни в коем случае нельзя, и в цикле освобождать - правильное решение.
delete применяется к указателям, память для которых была выделенна с помощью new, а delete[] применяется к указателям, память для которых была выделенна с помощью new[]. На первых порах этого достадочно. В принципе, на тему нужно ли delete[] и не достадочно ли просто delete, было много обсуждений, но по стандарту пока остается так. В основном разница в том, что выделяя память через new для классов, не нужно хранить количество выделенных элементов, чтобы знать сколько раз вызывать конструктор, и в delete не нужно их считывать. То есть, если оставить только delete, то это количество надо будет хранить всегда.
 
 Должен зказать, что в данном случае, по видимому, лучше использовать стандартные классы библиотеки С++, такие как std::vector, std::deque. В них можно запихивать объекты, и доставать их, вместе с тем не нужно заботиться об освобождении памяти - она будет освобождена автоматически при уничтожении контейнера.

Автор: brb 12.1.2003, 04:53
1. Массив поинтеров глобальный, но это действительно не важно.
2. Так как сколько всего мне их надо я не знаю и определяю в цикле, то и new[] использовать не могу.
3. Первоначальный вариант был написан, как бы я делал это через calloc и free на Си, но через new и delete. После нескольких итераций код улетал в никуда...
4. Прочитал в хэлпе:

#include <except.h>
#include <iostream.h>

void display(long double **);
void de_allocate(long double **);

int m = 3;                 // THE NUMBER OF ROWS.
int n = 5;                // THE NUMBER OF COLUMNS.

int main(void) {
  long double **data;

  try {                  // TEST FOR EXCEPTIONS.
     data = new long double*[m];        // STEP 1: SET UP THE ROWS.
     for (int j = 0; j < m; j++)
         data[j] = new long double[n];  // STEP 2: SET UP THE COLUMNS
  }
  catch (xalloc) {  // ENTER THIS BLOCK ONLY IF xalloc IS THROWN.
     // YOU COULD REQUEST OTHER ACTIONS BEFORE TERMINATING
     cout << "Could not allocate. Bye ...";
     exit(-1);
  }
  for (int i = 0; i < m; i++)
     for (int j = 0; j < n; j++)data[i][j] = i + j;// ARBITRARY INITIALIZATION
  display(data);
  de_allocate(data);
  return 0;
}

void display(long double **data) {
  for (int i = 0; i < m; i++) {
     for (int j = 0; j < n; j++)cout << data[i][j] << " ";
     cout << "\n" << endl;
  }
}

void de_allocate(long double **data) {
  for (int i = 0; i < m;  i++)delete[] data[i];// STEP 1: DELETE THE COLUMNS
  delete[] data;           // STEP 2: DELETE THE ROWS !!!!!!!!!!!!!!!!!
}
и сделал так же!!! Заработало.

Обрати внимание и тут new в цикле, а не new[], как ты пишешь!!!
А delete[] я взял в чистом виде.
Хорошо бы это проверить и на другом, не борландовском компиляторе.

Автор: Fantasist 12.1.2003, 15:20
Цитата
Обрати внимание и тут new в цикле, а не new[]


На самом деле, вот это:
Цитата
data = new long double*[m];

и вот это:
Цитата
data[j] = new long double[n];  

- это new[].

new - это вот так:

data[j][n] = new long double;

Так что, с точки зрения языка вышеприведенный код вполне верен. Разве что new бросает bad_alloc, а не xalloc.

Но повторю еще раз - лучше воспользоваться классами стандартной библиотеки - они весьма эффективны и гораздо понятнее и безопаснее.
Типа так (в простейшем варианте):

Код

#include <vector>
#include <iostream>

typedef vector<vector<long> > longMatrix;

void display(const longMatrix& matrix)
{
 for (int i = 0; i < matrix.size(); i++)
 {
    for (int j = 0; j < matrix[i].size(); j++)cout << matrix[i][j] << " ";
    cout << "\n" << endl;
 }
}

int main()
{
 longMatrix matrix;

 try
 {
   matrix.setsize(m)  
   for (int j = 0; j < m; j++)
     matrix[j].setsize(n);
 }
 catch(bad_alloc)
 { ...  }

 display(matrix);

 return 0;
}
 


Ничего самому освобождать не надо.

Автор: brb 12.1.2003, 17:34
во-первых, плохо понял твой пример. С классом матрица не знаком. Завера с утра повторю попытку.
во-вторых, это не ответ на вопрос почему не работает первоначальный код.
в-третьих, а сам попробовал мой код, который не?
Хотелось бы решать проблемы по мере возникновения, а не впрок.

Спасибо.

Автор: Fantasist 13.1.2003, 08:13
Цитата
С классом матрица не знаком

Это не класс матрицы - это класс массива (vector) мы делаем массив массивов, что не самый классный подход, но лучше, чем просто указатели.

Цитата
это не ответ на вопрос почему не работает первоначальный код.


Потому, что мне не понятно, что значит "не работает". Какие внешние признаки? При просмотре кода все выглядить вполне нормально.

Цитата
в-третьих, а сам попробовал мой код, который не

Это какой "не"? Но в любом случае, код буду пробовать только если очень интересно, а так лень, извините-с.

Цитата
Хотелось бы решать проблемы по мере возникновения, а не впрок


Впрок их решать гораздо лучше. :) Профилактика лучше лечения. Если уж взялись за ООП, надо использовать его по полной программе. Написать хотябы свой простенький класс матрицы. Примерно как http://www.forum.vingrad.ru/cgi-bin/newforum/ikonboard.cgi?act=ST;f=31;t=3633.

Автор: brb 13.1.2003, 11:21
Про ООП:
Их всех "мочить в сортире" надо как говорит российсктий президент, во главе и в первую очередь с Арафатом!!!:baaa
Про "не"
Вылетает по нарушению памяти после нескольких (3-4) проходов.   :notify

Автор: Fantasist 13.1.2003, 13:01
Цитата
Вылетает по нарушению памяти после нескольких (3-4) проходов


Я незнаю, насколько полно вы привели кусок кода (да и ошибки такие самому искать надо, с дебаггером) , однако вот тут:

Цитата
for (i = 0; pPayState[i] != NULL; i++ )delete pPayState[i];


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

Цитата
Их всех "мочить в сортире" надо как говорит российсктий президент, во главе и в первую очередь с Арафатом!!!


Это к чему? Если вы принципиально не любите ООП, то незачем брать С++, так как Си, на мой взгляд, балее мощьный системный язык.

Автор: brb 14.1.2003, 00:50
- фрагменты работы с памятью приведены дословно! Оба.
- это интересно! Т.е. я пытаюсь освободить СВОБОДНУЮ память! Тогда, наверняка, сработает защита памяти.
for (i = 0; pPayState[i] != NULL; i++ ){
  delete pPayState[i];
  pPayState[i] = NULL;
}
Странно, только, что 3-4 прохода, это полный размер pPayState[]. Вот если бы вылетало на втором...

- Попрошу не путать Object Oriented Programming(OOP), с которым я мирно сосуществую, и Организацию Освобождения Палестины(ООП), которую я действительно принципиально не признаю!
А насчет того, что С гораздее, я полностью согласен. Удалено модератором

Автор: Fantasist 14.1.2003, 01:07
Удалено модератором

Автор: Vit 14.1.2003, 01:47
Модератор: Пожалуйста, давайте обсуждать ООП в обоих смыслах во флейме, а целесообразность програмных технологий, если уж тах хочется, то отдельным топиком.

Автор: brb 14.1.2003, 03:29
С интересом бы прочитал этот коментарий! То, что взгляды не совпадают, явление нормальное, да и шутить тоже не грех, причем, на любую тему!

Автор: brb 27.1.2003, 03:23
Все замолчали на эту тему, а, по-моему, она не закрыта! Добавлю еще ряд подвопросов.
class PState{}
PState * ps[1200]; // статически выделенная память
for(i = 0; i <1200;i++)ps[i] = new PState ();
/*заполняем предыдущий статический массив динамически*/
/*
какой-то дурацкий код...
*/
delete [ ] ps;
/* похоже, что этот оператор освободит КАЖДЫЙ динамически выделенный элемент массива*/

// Теперь то же, но по-другому

PState **pps = new PState *[1200];
/*кстати, не понятно почему
PState *pps[ ] = new PState *[1200];
выдает ошибку - это же одно и то же!!!*/

for(i = 0; i <1200;i++)pрs[i] = new PState ();
/*заполняем динамический массив динамически*/
/*
еще какой-то дурацкий код...
*/
delete [ ] pps;
/*А что удаляет этот оператор? Массив указателей или и все что внутри тоже?
А может правильно делать так:
for(i = 0; i <1200;i++)delete pрs[i];
delete [ ] pps:
*/

Господа, пожалуйста, делитесь ОПЫТОМ и КОДОМ, а не соображениями.

Спасибо.

Автор: Fantasist 29.1.2003, 05:48
Цитата
Все замолчали на эту тему, а, по-моему, она не закрыта


Чем же это она не закрыта? На все предыдущие вопросы ответы были данны.

Автор: Fantasist 29.1.2003, 06:14
Цитата
class PState{}
PState * ps[1200]; // статически выделенная память
for(i = 0; i <1200;i++)ps[i] = new PState ();
/*заполняем предыдущий статический массив динамически*/
/*
какой-то дурацкий код...
*/
delete [ ] ps;
/* похоже, что этот оператор освободит КАЖДЫЙ динамически выделенный элемент массива*/


НЕЛЬЗЯ так делать! Я уже об этом говорил. delete[] применим ТОЛЬКО к указателям, для которых память была выделенна с помощью new[] . То, что на вашем компиляторе это может быть работает не означает, что так можно. Если в цикле выделяете, то и в цикле и освобождайте. То есть либо так:

Код

PState * ps[1200]; // статически выделенная память
for(i = 0; i <1200;i++)ps[i] = new PState ();
......
for(i = 0; i <1200;i++) delete ps[i];



либо так:

Код

PState * ps=new PState[1200]; // статически выделенная память
.......
delete [] ps;



Цитата
PState **pps = new PState *[1200];
/*кстати, не понятно почему
PState *pps[ ] = new PState *[1200];
выдает ошибку - это же одно и то же!!!*/


Ничего это не одно и то же, если вы пишите на С++. На мой взгляд, это извращение высшего порядка - в С++ классы есть. А тип PState *[] это не то же самое, что PState **, а то, что они оба реализуются как указатель на указатель, не делают их одним и тем же типом. Это в Java вы можете писать:

Сpd pd[];
pd=new Cpd[10];

В С++ это не так.

Цитата

А может правильно делать так:
for(i = 0; i <1200;i++)delete pрs[i];
delete [ ] pps;


Да. delete[] удаляет массив из нескольких элементов и для каждого из них вызывается деструктор, если тип нетривиальный. В данном случае - это массив указателей - тип тривиальный. Строчкой

delete [ ] pps;

массив указателей будет удален. Объекты, на которые эти указатели указывали никто удалять за вас не будет.

Автор: Guest 29.1.2003, 07:26
Уважаемый, Fantasist

С этого все начиналось...

PayState *ptr; // класс поинтер вестимо
PayState *pPayState[1200];

for (i = 0; pPayState[i] != NULL; i++ )delete pPayState[i];
// я думал, что тут я освобождаю всю занятую память
i = 0;
do {
ptr = new PayState ( bc, ic ); // конструктор - аллокирует новую запись
if ( ptr != NULL ){ // успешно
// какой-то дурацкий код...
pPayState[i++] = ptr; //Поместить в массив для дальнейших нужд
}
else {
ShowMessage ("Not enough memory, Sorry");
exit ( EXIT_FAILURE );
}
} while ( ); // больше записей нет!

Я начал эту тему потому что НЕ работало.
Скопированное из хэлпа
delete[ ] pPayState;
прекрасно освобождало память. Это и послужило отправной точкой дискуссии.
Ответа на почему не работало не нашлось, хотя гипотезы и предположения были. Обратите внимание, в НЕработающем коде я делал именно так, как вы настаиваете и я изначально делал.
Цитата
НЕЛЬЗЯ так делать! Я уже об этом говорил. delete[] применим ТОЛЬКО к указателям, для которых память была выделенна с помощью new[] . То, что на вашем компиляторе это может быть работает не означает, что так можно. Если в цикле выделяете, то и в цикле и освобождайте


Согласен, что некрасиво, но почему то что красиво не согласно со мной?
Код

PState **pps = new PState *[1200];
/*кстати, не понятно почему
PState *pps[ ] = new PState *[1200];
выдает ошибку - это же одно и то же!!!*/


Цитата

Ничего это не одно и то же, если вы пишите на С++. На мой взгляд, это извращение высшего порядка - в С++ классы есть. А тип PState *[] это не то же самое, что PState **, а то, что они оба реализуются как указатель на указатель, не делают их одним и тем же типом. Это в Java вы можете писать:

Сpd pd[];
pd=new Cpd[10];

В С++ это не так.


Мы о Джаве не говорим.
А вот
void main(int argc, char *argv[ ]){...}
void main(int argc, char **argv ){...}
Это что не C++? Или что-то из этого не? Или это не одно и тоже?
Не буду утверждать безаппеляционно, как некоторые, но замечу, что по определению [ ](массив) это *(указатель), и если есть
Код

char a[10];
char *p;
p = a; // абсолютно корректно, а  *( p + 5), например,  тоже самое что и a[5]

И причем тут классы? В С++ много чего есть! И при чем тут извращения?
Где сказано, что в ++ указатель на массив какого-либо типа и указатель на указатель того же типа, не одно и тоже? Единственный довод в Вашу пользу, что компилятор выдает ошибку, но это не самый сильный довод.

Цитата

Да. delete[] удаляет массив из нескольких элементов и для каждого из них вызывается деструктор, если тип нетривиальный. В данном случае - это массив указателей - тип тривиальный. Строчкой

delete [ ] pps;

массив указателей будет удален. Объекты, на которые эти указатели указывали никто удалять за вас не будет.


Похоже, что и это не так. Посмотрите опять на самое начало.

И давайте аргументировать, а не бросаться формулами.

С уважением,
BRB

Автор: Fantasist 31.1.2003, 03:09
Цитата
Я начал эту тему потому что НЕ работало.
Скопированное из хэлпа
delete[ ] pPayState;
прекрасно освобождало память


Слушайте, а чем вы компилируете? ? ?

Вот код:
Код

              CClass *pClass[1200];

for (int i=0; i<1200; ++i) {pClass[i]=new CClass(i);};

delete[] pClass;



Компилируем VS60. На последнюю строчку получаем:

Цитата
warning C4154: deletion of an array expression; conversion to pointer supplied


При исполнении все грохается на последней строчке, как и следовало ожидать. И это законно - освобождение стековой переменной.

Цитата
Согласен, что некрасиво, но почему то что красиво не согласно со мной?


Ну сколько можно! Это не не красиво, это НЕ ПРАВИЛЬНО по СТАНДАРТУ!

Цитата
но замечу, что по определению [ ](массив) это *(указатель), и если есть


Никогда такого опеределения не было. Поймите, С++ - это не С. Это разные языки, хоть и похожи.
В С++ вы неможете написать:

char p[]=new char[100];

Не можете, и все. Это СТАНДАРТ языка. ТО ЧТО МАССИВ(в реализации) ЯВЛЯЕТСЯ УКАЗАТЕЛЕМ НЕ МЕНЯЕТ ПОЛОЖЕНИЯ.

Удалено модератором

Автор: brb 31.1.2003, 04:32
Fantasist
Цитата

Слушайте, а чем вы компилируете? ? ?

Билдером. Был первый, сейчас обзавелся 6-м
Цитата

Компилируем VS60. На последнюю строчку получаем:

Может VC6?
Цитата

Никогда такого опеределения не было. Поймите, С++ - это не С. Это разные языки, хоть и похожи.

C++ это надмножество С. Не верите? Спросите у Страуструпа(фамилию, наверняка, переврал, но все равно узнаваемо), он ++ сочинил!
Цитата

В С++ вы неможете написать:
char p[]=new char[100];

Во-первых, частица НЕ с глаголами пишется раздельно, по крайней мере, на русском языке!
А, во-вторых, хотел бы узнать не то, что это ошибка /* Cannot convert 'char *' to 'char[]'*/, а почему это так!
Цитата

При исполнении все грохается на последней строчке, как и следовало ожидать. И это законно - освобождение стековой переменной.

Массив указателей был объявлен СТАТИЧЕСКИ! Может это причина? Почему же, все-таки первый вариант(внутри цикла до NULL) не работал? И мы именно с Вами это уже обсуждали!
Цитата

for (i = 0; pPayState[i] != NULL; i++ ){
  delete pPayState[i];
  pPayState[i] = NULL;
}
Странно, только, что 3-4 прохода, а это полный размер pPayState[]. Вот если бы вылетало на втором...

Последнее замечание оставленно Вами без ответа.
Тем не менее, от добра добра не ищут, и я оставил все как есть, потому что нет проблем!
Удалено модератором

Автор: Unregistered 31.1.2003, 06:45
Удалено модератором

Автор: brb 31.1.2003, 07:29
Удалено модератором

Автор: Fantasist 31.1.2003, 07:56
Удалено модератором

Цитата
А, во-вторых, хотел бы узнать не то, что это ошибка /* Cannot convert 'char *' to 'char[]'*/, а почему это так!


Вот %^&$!! ДА ПОТОМУ ЧТО ЭТО РАЗНЫЕ ТИПЫ ОПРЕДЕЛЕННЫ СТАНТДАРТОМ ЯЗЫКА Mассив - это статический (причем с фиксированным размером!) кусок памяти и все тут. Исключения составляют формальные аргументы функций.

Удалено модератором

Автор: brb 31.1.2003, 08:37
Удалено модератором

Автор: Vit 31.1.2003, 09:00
Модератор: ещё не забыли о чём спрашивалось в теме? Напоминаю -

Цитата
объясните в чем дело? Я и синтаксиса такого не знаю: delete[] pPayState;


2 brb: если на вопрос отвечено, то я тему закрываю, если нет, то спрашивайте по существу чтО именно не понятно.

2 Fantasist: Давайте без перехода на личности

Автор: Fantasist 31.1.2003, 09:07
Удалено модератором

Автор: brb 31.1.2003, 09:26
Vit
Считаю действия модератора не вполне корректными. Лозунг "Уважайте труд уборщиц" к действиям
Цитата
Удалено модератором
не подходит.
Продолжаю считать губость и хамство недопустимыми, даже если кому-то и хочется поругаться.
Давайте писать грамотно чтобы ляпов типа
Цитата
балее мощьный системный язык.
не было. Знание языков программирования НЕ освобождает от знания родного языка!


Автор: Vit 31.1.2003, 09:57
Модератор: Обсуждение модераторов и правомерности их действий проводится в разделе обсуждение форума. О моих действиях Вы можете отзываться как угодно, но я удалил только те высказывания, которые не имели никакого отношения к обсуждению заданного вопроса. Я что-то не понял, чем Вас не устроило моё модерирование, ведь Вы написали о том что по отношению к Вам были проявлены "губость и хамство" - я удалил все такие замечания, кроме того я удалил все рассуждения которые выходили за рамки заданного вопроса, я сделал личное замечание Fantasist'у о недопустимости таких высказываний. Считаю эти меры достаточными, но если в этот раздел попадёт хоть ещё одна фраза не относящаяся к программированию на C/C++ от Fantasist или Brb, то топик будет закрыт.

Теперь, я указал в своём сообщении, что пора бы прекратить пререкания и обсуждения личностей, и вообще все вопросы не связанные с темой топика. Оба оппонента не вняли предупреждению и продолжается обсуждение непонятно чего! Ещё раз повторюсь, здесь обсуждается программирование на С и С++ и среды разработки програм на С/С++. Обсуждение любых других тем, в том числе других участников, действий модератора и грамотность языка в этом разделе категорически запрещено. Пожалуйста - всё это Вы можете делать в разделах о форуме и флейме.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)