IPB
ЛогинПароль:

> Внимание!

1. Пользуйтесь тегами кода. - [code] ... [/code]
2. Точно указывайте язык, название и версию компилятора (интерпретатора).
3. Название темы должно быть информативным.
В описании темы указываем язык!!!

Наладить общение поможет, если вы подпишитесь по почте на новые темы в этом форуме.

3 страниц V  1 2 3 >  
 Ответить  Открыть новую тему 
> тип string, утечка памяти, C++ Builder 6
сообщение
Сообщение #1


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


Работу с типом char* я тупо не смог осилить, так как так и не понял, при каких операциях надо выделять ему память, при каких не надо. Короче ужаснейший тип, и кто его придумал, и ну его нафиг.
Решил применить string - я так понял, что это аналог паскалевского. Но обнаружилось, что после динамического создания структуры, содержащей поле типа string, и её удаления кол-во занимаемой памяти меняется. Встатив этот кусок в бесконечный цикл, я обнаружил, что память, пожираемая программой, пульсирует от 10 до 50 MЬ.
Вторая проблема - при отладке на операциях с этим типом он лезет в свои модули, мне они неинтересны, как запретить отладчику в них влазить.
Блин, настроек по сравнению с Дельфой раза в 3 больше, на кой.

Сообщение отредактировано: TarasBer -


--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #2


Гость






Цитата
так и не понял, при каких операциях надо выделять ему память, при каких не надо.
Выделять память надо всегда. Просто когда-то ее можно выделить самим фактом инициализации строки:
char *s = "my string";
, а иногда это приходится делать через new, то есть выделять память динамически.

Цитата
после динамического создания структуры, содержащей поле типа string, и её удаления кол-во занимаемой памяти меняется.
Можно посмотреть на структуру, и на то, как выделяется память? Что говорят средства отладки (скажем, тот же CodeGuard)?

Добавлено через 1 мин.
Цитата
Вторая проблема - при отладке на операциях с этим типом он лезет в свои модули, мне они неинтересны, как запретить отладчику в них влазить.
Не делать "Step Into", а пользоваться вместо этого "Step Over", тогда отладчик будет выполнять всю строку, а не по отдельным операциям.
 К началу страницы 
+ Ответить 
сообщение
Сообщение #3


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


Цитата(volvo @ 18.09.2009 17:49) *

Выделять память надо всегда. Просто когда-то ее можно выделить самим фактом инициализации строки:
char *s = "my string";
, а иногда это приходится делать через new, то есть выделять память динамически.


Но просто s = "my string" уже не катит. Что делают с памятью strcpy и strcat - тоже непонятно.

Цитата


Можно посмотреть на структуру, и на то, как выделяется память? Что говорят средства отладки (скажем, тот же CodeGuard)?



Ну например:
Код

struct S {
    string Name;        
};
...
S *s;
while(1){
    s = new S[8];
    delete s;
};

Прикол в том, что если заменить string на int, то утечка полностью исчезает.

В кодгуарде пока не разобрался.

Цитата



Добавлено через 1 мин.
Не делать "Step Into", а пользоваться вместо этого "Step Over", тогда отладчик будет выполнять всю строку, а не по отдельным операциям.

А если строка передаётся как параметр в функцию? Тогда степ овер пропусткает то, что делает функция, а это как раз не надо. А степ инто тут же залезает в дебри всякие.

Сообщение отредактировано: TarasBer -


--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #4


Гость






Цитата
Но просто s = "my string" уже не катит.
Естественно... Для работы со строками в стиле С существуют функции, начинающиеся на str... В данном случае тебе нужен strcpy.

Цитата
Что делают с памятью strcpy и strcat - тоже непонятно.
С чего бы? Ничего они не делают с памятью. В описании strcpy ясно написано, что "для того, чтобы избежать порчи памяти из-за переполнения, указатель destination должен указывать на область памяти достаточного объема". То есть, выделить необходимую память перед копированием - забота программиста.

То же самое касается и strcat, destination должен указывать на область памяти достаточного объема, чтобы вместить результирующую строку и завершающий символ '\0'.

Цитата
Прикол в том, что если заменить string на int, то утечка полностью исчезает.
Нет, прикол не в этом. А в том, что если ты выделяешь память под МАССИВ структур, то и удалять надо МАССИВ структур:
struct S {
string Name;
};
...
S *s;
while(1){
s = new S[8];
delete [] s;
} // здесь точка с запятой не нужна, кстати

А почему утечка исчезает при замене string на int - это отдельный разговор, сли интересно - я расскажу, а просто так не хочется по клавишам стучать.
 К началу страницы 
+ Ответить 
сообщение
Сообщение #5


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


Цитата(volvo @ 18.09.2009 19:23) *

С чего бы? Ничего они не делают с памятью. В описании strcpy ямсно написано, что "для того, чтобы избежать порчи памяти из-за переполнения, указатель destination должен указывать на область памяти достаточного объема". То есть, выделить необхпдимую память перед копированием - забота программиста.


И ВСЕ стандрртные функции, возвращающие char* - тоже требуют предварительного выделения памяти? Ладно, тогда вроде понятнее. Попробую эти char* ещё раз расписать. А то что все пишут на C и не жалуются, а я не всё не врубаюсь, всё время кажется, что меня Паскаль испортил.

Цитата

Нет, прикол не в этом. А в том, что если ты выделяешь память под МАССИВ структур, то и удалять надо МАССИВ структур:
Код

...
  delete [] s;
} // здесь точка с запятой не нужна, кстати


Вот ведь оно как, так и знал, что какую-то закорючку забыл поставить.
А лишние точки с запятой, не вызывающие ошибок компиляции - разве вредят?
Цитата

А почему утечка исчезает при замене string на int - это отдельный разговор, сли интересно - я расскажу, а просто так не хочется по клавишам стучать.

Интересно.

Хотя не, такой момент ещё непонятен. Если у формы есть свойство Caption, то надо писать Caption = IntToStr(i) или сначала S = IntToStr(i), а потом Caption = S, где S - промежуточная строка, под которую выделена память?

Сообщение отредактировано: TarasBer -


--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #6


Гость






Цитата
А лишние точки с запятой, не вызывающие ошибок компиляции - разве вредят?
До поры до времени - не вредят... Пока ты не напишешь какую-нибудь конструкцию, в которой эта самая точка с запятой, скажем, образует пустой цикл, пустую ветку if/else или еще что-нибудь такое же трудноуловимое. Синтаксис С очень многое позволяет "намудрить", поэтому лучше избавляться от привычки ставить лишние символы, даже если они не вызывают ошибок компиляции.

Цитата
Интересно.
Ну, тогда смотри: при выполнении delete, если добавить [], как я сделал, происходит вот что: сначала для каждого элемента массива вызывается его деструктор, и только потом освобождается память, выделенная под сам массив. Что происходит в случае замены string на int? А ничего страшного не произойдет, даже если не вызвать деструктор для int, от него все равно толку нет, утечки не будет даже без его вызова. В случае string все серьезнее: это полноправный класс, который имеет конструктор, выделяющий память. И экземпляр этого класса требуется удалить, иначе сам экземпляр остается висеть в памяти, а указатель на него потеряется.

Поэтому всегда, когда выделяешь память динамически под массив, либо состоящий из не POD-типов (Plain Old Data, данные в стиле С), либо содержащий такие типы (как у тебя - структура, которая содержит non-POD type), то освобождай ее всегда с использованием delete [] p_arr;, чтобы быть уверенным в том, что для каждого элемента вызовется деструктор, и только потом удалится сам массив.
 К началу страницы 
+ Ответить 
сообщение
Сообщение #7


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


Цитата(volvo @ 18.09.2009 20:05) *


Ну, тогда смотри: при выполнении delete, если добавить [], как я сделал, происходит вот что: сначала для каждого элемента массива вызывается его деструктор, и только потом освобождается память


Это, я так понял, только для не-POD типов, потому что для массива объектов для каждого элемента деструктор надо вызывать руками?
Решил вместо char* работать с AnsiString, а то у всех компонентов свойства как раз такого типа.


--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #8


Гость






Цитата
Это, я так понял, только для не-POD типов, потому что для массива объектов для каждого элемента деструктор надо вызывать руками?
Угу, именно поэтому...

Кстати, а что за задачу ты решаешь? Может, будет выгоднее не выделять самому динамически память под массив, а воспользоваться либо vector<AnsiString> либо vector<S>? Тут ведь мало того, что не надо выделять память вручную, так еще и удалять вектор не надо, он описывается статически, следовательно при выходе из области видимости самоликвидируется (причем корректно, перед этим вызвав деструктор каждого своего элемента).
 К началу страницы 
+ Ответить 
сообщение
Сообщение #9


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


Цитата(volvo @ 18.09.2009 21:53) *

Кстати, а что за задачу ты решаешь?


Я решаю задачу освоения нового для меня и довольно хитрого языка, за который к тому же много где платят, в отличие от Дельфей (увы, реальность примерно такова). Пока впечатление отвратительное.
На примере калькулятора со встроенным парсером и ещё некоторыми наворотами (он уже знает, что 2+2*2 равно 6). Тем более, что опыт распознавания выражений имею.
Собсна та структура, содержащая строчку - это описание функций, содержащее как раз и имя, и ссылку на операцию, и ещё много чего.

Цитата

Может, будет выгоднее не выделять самому динамически память под массив, а воспользоваться либо vector<AnsiString> либо vector<S>? Тут ведь мало того, что не надо выделять память вручную, так еще и удалять вектор не надо, он описывается статически, следовательно при выходе из области видимости самоликвидируется (причем корректно, перед этим вызвав деструктор каждого своего элемента).


Ну я так понял, что фишка С++ в полной открытости всех действий перед программистом. И мне кажется, что использовать в нём подобные вещи - всё равно что покупать джип для езды по городу.


--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #10


Гость






Фишка С++ - не в том, чтобы не писать свой велосипед каждый раз, да еще и чинить его потом на каждом повороте, а в том, что ты берешь отлаженную сотнями и тысячами программистов библиотеку (я про STL) и просто реализуешь свою задачу. Нет, дело твое, конечно, но потом не говори, что написание программ на С++ занимает ОЧЕНЬ долгое время. Оно занимает просто долгое время, а не ОЧЕНЬ. ОЧЕНЬ - это из-за того, что ты опять с нуля реализуешь то, что уже есть в языке (хотелось бы напомнить, что STL - это часть Стандарта C++).

По поводу "джипов". Лучше я буду ездить везде на джипе, чем каждый раз менять транспортные средства (доехал до перекрестка, а там - лужа. Упс... Надо пересесть в амфибию, а то на велосипеде и утонуть можно). Хотя ко мне это относится меньше всего. Ада - язык универсальный, причем с Паскалевским синтаксисом, что снимает огромную часть проблем, да и оплачивается программирование на нем в разы (если не на порядки) выше, чем программирование на С++. И средств, подобных тому же STL-ю там не меньше, а даже больше... Так что я свой джип уже нашел. Передвигается одинаково уверенно и по Win, и по Lin, и по Embedded, и вообще практически по любым существующим территориям. Хочешь попробовать догнать? Но это уже оффтоп в данной теме.
 К началу страницы 
+ Ответить 
сообщение
Сообщение #11


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


Цитата(volvo @ 18.09.2009 22:29) *

Фишка С++ - не в том, чтобы не писать свой велосипед каждый раз, да еще и чинить его потом на каждом повороте, а в том, что ты берешь отлаженную сотнями и тысячами программистов библиотеку (я про STL) и просто реализуешь свою задачу.


Вот как раз для такого куда лучше бы подошёл какой-нибудь другой язык, пусть не с таким оптимизированным кодом, как говорят про C++, зато более дружелюбный к программисту. А иначе зачем вообще этот C++ нужен?


--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #12


поиск
****

Группа: Пользователи
Сообщений: 347
Пол: Мужской
Реальное имя: nir

Репутация: -  2  +


С++ наоборот ИМХО понятен, если программа написана с высоким и правильным содержанием STL
компонентов.(к сожалению, мои программы этим пока не очень отличаются).
std::vector<car> вектор из машин)


--------------------
typedef void Śūnyatā ;
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #13


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


Хьюстон, у нас опять проблемы.
При вводе корректных выражений утечка прекратилась, но при вводе "1/0" память опять потекла. Само деление обёрнуто в

try {
}
catch (...) {
};



Внутри трая само деление, внутри катча присвоение пустого значения.


--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #14


Гость






Где память выделяется, и где она освобождается? Если сделать так:
	S *s = new S[8];
try
{
//
int i = 1, j = 0;
int x = i / j;
}
catch(...)
{
// Ну, по-хорошему, тут надо еще уточнить что за исключение
ShowMessage("Division by Zero");
}
delete [] s;
, то утечки не будет... Покажи, как делаешь ты...
 К началу страницы 
+ Ответить 
сообщение
Сообщение #15


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


Естественно, все выделения и освобождения находятся ВНЕ блока.
Чтобы указывать исключение, надо запоминать их список и названия, а операций, которые могут вызвать то или иное исключение, довольно много. Тот же логарифм, например. Проще ловить любое.
Полностью блок выглядит так:

TComplex operator / (TComplex X, TComplex Y) {
TComplex Z;
try {
if (X.IsValue && Y.IsValue) {
Z.IsValue = true;
long double D = 1 / (Y.Re * Y.Re + Y.Im * Y.Im);
Z.Re = D * (X.Re * Y.Re + X.Im * Y.Im);
Z.Im = D * (-X.Re * Y.Im + X.Im * Y.Re);
} else {
Z.IsValue = false;
};
} catch (...) {
Z.IsValue = false;
};
return Z;
};



--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #16


Гость






Ну, хорошо... Это переопределение операции деления. Для того, чтобы у тебя была утечка, надо, чтобы память выделялась и не освобождалась, так? Вот я и хочу увидеть, как ты вызываешь эту самую операцию деления, и что происходит в ее ближайшем окружении, после чего у тебя теряется память...

Исключение поймано, IsValue результата установлено в false, все нормально. Дальше все зависит от того, как именно ты обрабатываешь этот IsValue...
 К началу страницы 
+ Ответить 
сообщение
Сообщение #17


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


Значит собсна вычисление выражения выглядит так:

void TTree::GetValue() {
if (Data.Func) {
for (int i = 0; i < Count; i++) {
Child[i]->GetValue();
};
Data.Value = Data.Func->Operation(Count, Child);
};
};


Тут пока так сделано, что если указатель Data.Func нулевой, то значит эта вершина содержит константу и содержимое Value и так уже правильное. Сам Data.Func, если не нулевой, ссылается на элемент из заранее созданного массива, то есть его создавать и удалять не надо.
Счётчик объектов TTree говорит, что после все действий объектов ноль - то есть тут тоже всё в порядке.
Operation - это указатель на тип-функцию:


typedef TComplex(TTreeFunc)(int Count, TTree **Args);

struct TFunc {
...
TTreeFunc *Operation;
};



Собсно сама операция, которая тут вызывается:


TComplex Div(int Count, TTree **Args) {
TComplex Result;
if (Count != 2) {
Result.IsValue = false;
} else {
Result = Args[0]->Data.Value / Args[1]->Data.Value;
};
return Result;
};



Тоже ничего не создаётся.


--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #18


Гость






Цитата
Счётчик объектов TTree говорит, что после все действий объектов ноль - то есть тут тоже всё в порядке.
Сомневаюсь... Поскольку сведения у меня о твоей программе только частичные - попробовал сделать так:
TComplex Div(int Count, TComplex *Args) {
TComplex Result;
if (Count != 2) {
Result.IsValue = false;
} else {
Result = Args[0] / Args[1];
};
return Result;
};

void __fastcall TForm1::Button1Click(TObject *Sender)
{
TComplex *f = new TComplex[2];

TComplex c = Div(2, f);
delete [] f;

ShowMessage(BoolToStr(c.IsValue, true));
}
Все нормально, утечек нет... Значит проблема-таки с TTree где-то. Подключи уже CodeGuard (у меня в Builder 2009 для этого надо установить Project -> Options -> C++ Compiler Debugging -> Enable CodeGuard в True, как это делается в BCB 6 - не помню), и посмотри, что он тебе говорит, где именно утечка?
 К началу страницы 
+ Ответить 
сообщение
Сообщение #19


Злостный любитель
*****

Группа: Пользователи
Сообщений: 1 755
Пол: Мужской

Репутация: -  62  +


Запуск с гуардом даёт аксесс виолейшн, программа намертво виснет, не желает завершаться по команде из среды, завершается только проводником, причём так как среда об этом не узнаёт, для перекомпиляции приходится перезапускать среду.
Я не пойму, что за такая проблема с TTree может быть, которая с 1/1 не вылезает, а с 1/0 вылезает.


--------------------
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
сообщение
Сообщение #20


Гость






Присоедини свой проект, я его прогоню в 2009... Можно в приват, если не хочешь выкладывать в общий доступ.
 К началу страницы 
+ Ответить 

3 страниц V  1 2 3 >
 Ответить  Открыть новую тему 
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 





- Текстовая версия 27.10.2020 11:19
500Gb HDD, 6Gb RAM, 2 Cores, 7 EUR в месяц — такие хостинги правда бывают
Связь с администрацией: bu_gen в домене octagram.name