Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 17.09.2012, 14:04   #1
Homa_1983
Пользователь
 
Регистрация: 14.10.2011
Сообщений: 29
По умолчанию освобождение памяти

Здравствуйте. Есть два варианта кода:
1.
Код:
char** pointer;
...................................
pointer = (char**) malloc(N * sizeof(char*));
for(i = 0; i < N; i++)
{
	pointer[i] = (char*) malloc(10 * sizeof(char));
}

if(pointer != 0)
{

	...................................
	...................................
	...................................

	for(i = 0; i < N; i++)
	{
		free(pointer[i]);
	}
			
	free(pointer);
}
2.
Код:
char** pointer;
...................................
pointer = malloc(N * sizeof(char));
for(i = 0; i < N; i++)
{
	pointer[i] = malloc(10 * sizeof(char));
}

if(pointer != 0)
{

	...................................
	...................................
	...................................

	for(i = 0; i < N; i++)
	{
		free(pointer[i]);
	}
			
	free(pointer);
}
В обоих случаях компилятор ошибки не дает, но при исполнении программы во 2 случае возникает ошибка связанная с освобождением памяти.
Объясните, пожалуйста.

Последний раз редактировалось Homa_1983; 17.09.2012 в 14:06. Причина: ошибка в формулировке вопроса
Homa_1983 вне форума Ответить с цитированием
Старый 17.09.2012, 16:42   #2
f.hump
C/C++, Asm
Участник клуба
 
Аватар для f.hump
 
Регистрация: 02.03.2010
Сообщений: 1,323
По умолчанию

ничего удивительного. второй вариант неправильный: память (pointer) выделяется под N символов, а не N указателей. и в принципе, при достаточно большом N, программа будет ламаться еще до освобождения памяти.

Последний раз редактировалось f.hump; 17.09.2012 в 16:44.
f.hump вне форума Ответить с цитированием
Старый 31.08.2013, 03:35   #3
Vladiger
Пользователь
 
Регистрация: 31.08.2013
Сообщений: 93
По умолчанию

Доброго времени суток. Не стал создавать новую тему, немного порылся в старых, нашел более подходящую, для моего "ламерского" вопросика...
Ребят, я новичок в програмировании (не кидайтесь тапками), потихоньку изучаю, разбираюсь и.т.д и.т.п...

Вопрос собственно вот в чем:
Как правильно освобождать память если она выделена не для одной переменной, а для целой структуры например или класса, которые в свою очередь так же содержат переменные с ссылками и выделенной для них памятью?

Ниже пример кода с двумя вариантами освобождения памяти. Какой из них правильный?

Код:
#include <iostream>

using namespace std;

struct MyStruct {
	wchar_t* strKeyName;
	wchar_t* strKeyValue;
};

int main() {

	// Выделяем память для структуры и её переменных
	MyStruct* s = new MyStruct;
	s->strKeyName = new wchar_t[32];
	s->strKeyValue = new wchar_t[32];

	// Заполняем переменные данными
	wcscpy_s(s->strKeyName, 32, L"String KeyName:");
	wcscpy_s(s->strKeyValue, 32, L"String Name:");

	wcout << s->strKeyName << "\n" << s->strKeyValue << endl;

	// Освобождение памяти, вариант 1
	delete s;

	/* Освобождение памяти, вариант 2
	delete[] s->strKeyName;
	delete[] s->strKeyValue;
	delete s;
	*/

	system("Pause");
	return 0;
}
Vladiger вне форума Ответить с цитированием
Старый 31.08.2013, 09:28   #4
SAMOUCHKA
Форумчанин
 
Регистрация: 07.08.2011
Сообщений: 576
По умолчанию

Второй вариант правильный.
А вообще с классом проще было бы. Например можешь в конструкторе выделить память, в деструкторе освободить.

Последний раз редактировалось SAMOUCHKA; 31.08.2013 в 09:49.
SAMOUCHKA вне форума Ответить с цитированием
Старый 31.08.2013, 10:20   #5
Vladiger
Пользователь
 
Регистрация: 31.08.2013
Сообщений: 93
По умолчанию

Цитата:
Например можешь в конструкторе выделить память, в деструкторе освободить.
Да, спасибо! Об этом я знаю. Собственно структура в этом плане так же может содержать деструктор как и класс. И определить её можно например так:

Код:
struct MyStruct {
	wchar_t* strKeyName;
	wchar_t* strKeyValue;
	~MyStruct() {
		delete[] strKeyName;
		delete[] strKeyValue;
	}
};

int main() {
	MyStruct* s = new MyStruct;
	s->strKeyName = new wchar_t[32];
	s->strKeyValue = new wchar_t[32];
	delete s;
	return 0;
}
В этом случае, в дальнейшем написании программы уже не придется задумываться все ли ссылки содержащиеся в структуре были удалены. После delete s запустится деструктор структуры и освободит память.

Но меня интересовал как раз первый вариант. Произойдет ли утечка памяти, если структура будет удалена без освобождения её внутренних ресурсов?
Vladiger вне форума Ответить с цитированием
Старый 31.08.2013, 10:47   #6
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,331
По умолчанию

Цитата:
Сообщение от Vladiger Посмотреть сообщение
Произойдет ли утечка памяти, если структура будет удалена без освобождения её внутренних ресурсов?
Естественно.
Если бы нет, тогда не надо было бы удалять и саму структуру.
waleri вне форума Ответить с цитированием
Старый 31.08.2013, 11:48   #7
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Цитата:
Сообщение от Vladiger Посмотреть сообщение
Да, спасибо! Об этом я знаю. Собственно структура в этом плане так же может содержать деструктор как и класс. И определить её можно например так:

Код:
struct MyStruct {
	wchar_t* strKeyName;
	wchar_t* strKeyValue;
	~MyStruct() {
		delete[] strKeyName;
		delete[] strKeyValue;
	}
};

int main() {
	MyStruct* s = new MyStruct;
	s->strKeyName = new wchar_t[32];
	s->strKeyValue = new wchar_t[32];
	delete s;
	return 0;
}
В этом случае, в дальнейшем написании программы уже не придется задумываться все ли ссылки содержащиеся в структуре были удалены. После delete s запустится деструктор структуры и освободит память.

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

Если угодно - для упрощения и автоматизации многих вещей.

Вот например, ваш код можно переписать:

Код:
struct MyStruct {

        MyStruct(const size_t n) {
	        strKeyName = new wchar_t[n];
	        strKeyValue = new wchar_t[n];
        } 
 
	~MyStruct() {
		delete[] strKeyName;
		delete[] strKeyValue;
	}
	wchar_t* strKeyName;
	wchar_t* strKeyValue;

};

int main() {
	MyStruct* s = new MyStruct(32);
	delete s;
	return 0;
}
Теперь вам уже не нужно в ручную контролировать мемберы класса. Он самостоятельно выделит столько , сколько вы ему скажите. И самостоятельно удалит, когда закончится его время жизни.

Можно пройти в этом направлении дальше:

Код:
 #include<memory>

        struct MyStruct {

        MyStruct(const size_t n) {
	        strKeyName = new wchar_t[n];
	        strKeyValue = new wchar_t[n];
        } 
 
	~MyStruct() {
		delete[] strKeyName;
		delete[] strKeyValue;
	}
	wchar_t* strKeyName;
	wchar_t* strKeyValue;

};

int main() {

        std::unique<MyStruct> s( new MyStruct(32) );
	return 0;
}
Теперь умный указатель unique сам позаботится о том, что бы в нужный момент вызвать delete

На языке с++ есть множество способов, которые позволяют программисту вообще не думать о том, как работает динамическая память, и нигде самому не дергать new/delete полностью переложив эту ответственность на умную автоматику.

Собственно само объектно ориентированное программирование выполняется по принципу: создать удобный класс-инструмент, работая с которым не нужно думать о том, как он устроен внутри. И не нужно думать о том, как он будет за собой все подчищать.
_Bers вне форума Ответить с цитированием
Старый 31.08.2013, 14:36   #8
Vladiger
Пользователь
 
Регистрация: 31.08.2013
Сообщений: 93
По умолчанию

Цитата:
создать удобный класс-инструмент, работая с которым не нужно думать о том, как он устроен внутри
Этим я как раз и занимаюсь, но с учетом того что С++ богат изощеренностями о которых мне пока не ведомо, приходится задавать "элементарные" (так сказать базовые) вопросы, как раз для того, что бы и писать такие удобные в использовании классы. При том не только удобные, но ещё и производительные...

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

В Вышеприведенном примере, что бы не дёргать память структуру проще было вообще объявить как
Цитата:
struct MyStruct {
wchar_t strKeyName[32];
wchar_t strKeyValue[32];
};
Но я копаю немного в другом направлении. В данный момент я пишу парсер INFO или XML файлов (не важно, любой) и возникают немного другие вопросы, не только про память, а ещё и про скорость выполнения и целесообразность.

К примеру чтение INI файлов WinAPI функциями GetPrivateProfileString() и WritePrivateProfileString() очень удобна, там и парсить ничего не нужно, функции сами выделяют [секция] -> [ключ] -> [значение]. Но есть одно НО. Эти функции читают только один ключ за одно обращение, а это подразумевает 1) Открыть файл 2) Просканировать строки 3) Закрыть файл и все это ради того что бы получить только один ключ.
А если в INI файле 5000 ключей? Это чего 5000 раз один и тот же файл на диске дёргать?

А не проще ли взять весь файл в буфер, распарсить его по нодам и ключам и занести в динамический массив, под который выделить временную память, до того момента пока все эти данные будут прочитаны и станут не нужны.
Или вот ещё другие парсеры мне попадались, я разбирал их код (ну например boost property_tree и ещё что то попадалось) и все они работают по такому же принципу 1) открывают файл 2) читают одно значение 3) закрывают файл.

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

Возникает дилема: - Простота реализации против ресурсозатрат и быстродействия. И что лучше я пока не знаю.
Бегать 5000 раз по строкам при помощи std::string::find() конечно намного проще, чем один раз пробежаться for() по массиву текстового буфера и с ходу определить все ноды, ключи и значения по своим местам, для которых выделить память вручную, что бы далее к ним можно было обращаться хоть 500000 раз, но уже не сканируя, а напрямую по индексам (ссылкам)...

Сорри за тавтологию, парсер в этой теме вобщем то и не при чем, просто в данный момент меня заинтересовало именно выделение и освобождение памяти, что бы оценить целесообразность всех этих затрат.
Может действительно не стоит экономить на нескольких миллисекундах и обращениях к HDD, что бы не потерять ни единого байта памяти, котрая "Ой как может понадобиться" в каких нибудь ресурсоемких пректах!
Vladiger вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Освобождение памяти Seran4ek Общие вопросы Delphi 7 21.12.2009 18:07
Освобождение памяти PUH Помощь студентам 1 22.11.2009 17:14
Освобождение памяти VadEr Общие вопросы Delphi 2 17.04.2009 22:23
Освобождение памяти AlexandrSid Общие вопросы Delphi 3 02.02.2009 13:45
Освобождение Памяти в Си volotsky Помощь студентам 2 16.12.2008 22:36