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


Автор: bern 2.12.2005, 23:54
Здраствуйте! У меня к вам такая просьба - объясните мне пожалуйста чем таким кардинальным отличается динамическая память от массивов и что можно сделать с помощью new&delete чего нельзя было бы сделать с помощью массивов? Ведь в динамической памяти точно также выделяется место под отдельные переменные и массивы, так в чём разница ? И вообще почему этот метод называется динамическим?

Автор: blackofe 3.12.2005, 00:26
Цитата(bern @ 2.12.2005, 23:54)
Здраствуйте! У меня к вам такая просьба - объясните мне пожалуйста чем таким кардинальным отличается динамическая память от массивов и что можно сделать с помощью new&delete чего нельзя было бы сделать с помощью массивов? Ведь в динамической памяти точно также выделяется место под отдельные переменные и массивы, так в чём разница ? И вообще почему этот метод называется динамическим?

в c++ массивы должны иметь константную размерность. динамическое выделение памяти позволяет объявить массив произвольного размера (который, вообще говоря, не известен на этапе компиляции).

Автор: Dray 3.12.2005, 00:32
Если делаешь так:
Код

int a = 0;

То а создается в стеке.
Если так:
Код

int *a = new int;
*a=0;
delete a;

То а создается в куче.
Разница в том, что переполнение стека куда реальнее чем переполнение кучи.
Вообще указатели и динамическое распределение памяти чаще используются для структур данных самодельных например в списках:
Код

class CNode
{
public:
 int data;
 CNode *pNext;
 CNode ()
 {
  data = 0;
  pNext = NULL;
 };
};

void main (void)
{
 CNode node;
 node->data = 12; //Например
 node->pNext = new CNode;
 //Вообще это отдельная тема
};

Если интересно про списки то лучше воспользоваться поиском. Это уже обсуждалось.
Динамической она называется поскольку количество требуемой памяти определяется во время выполнения программы. Например при создании масива так:
Код

const n = 10;
int mas[n];

Нужно заранее знать кол-во элементов массива. И использовать только константу.
А если так:
Код

int mysize = 0;
cin >> misize;
int *mas = new int[mysize];

Не обязательно знать колличество элементов заранее можно ввести прям с клавиатуры. Память распределяется динамически.

Автор: Aleksandor 3.12.2005, 00:32
Динамический способ рулит потому что-
а) экономит память (и за счет этого повышает быстродействие программы). Часто это вообще критично для программы.
б) не загромождает стек - массив (статический) как любая другая переменная передается в стеке и если он велик то произойдет переполнение стека отведенного потоку и приложение рухнет smile

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

new только один из способов, в Win API используем HeapAlloc и VirtualAlloc

Пример недостатки статического выделения памяти-
char array[1000][1000]; // массив 1000 строк по 1000 байт каждая

- если массив не в глобальной памяти, то он вызовет переполнение стека
- каждая строка может содержать всего 1 символ (+нулевой байт), итого 2 байта.
остальные 998000 байт расходуются впустую
- длина строки может превысить 1000 байт, тогда мы не сможем впихнуть ее в массив
- мы можем не знать заранее сколько строк потребуется, может всего одна, а может 1001,
в первом случае мы зря расходуем до 999998 байт, во втором вообще труба. Нельзя всунуть больше 1000 строк в этот массив!

Динамическое выделение памяти снимает все эти проблемы smile


Автор: maxim1000 3.12.2005, 16:02
ну и контроль времени жизни еще можно упомянуть
с точки зрения памяти время жизни - время, когда объект занимает память
в случае использования стека время жизни контролируется тем, когда выполнение входит в блок, в котором объявлен объект, и тем, когда выходит

в случае с динамическим распределением можно удалять объект раньше или позже в зависимости от, например, булевой переменной...

Автор: Guest 3.12.2005, 19:23
Спасибо за отеты.Можно ещё один вопрос , хоть и не в тему , но создавать новый топик не хочется.Есть вот такой код:
[code=cpp]

#include <iostream.h>
#include <string.h>

class Person
{
public:
Person(char *pN)
{
cout << "Sozdayom " << pN << "\n";
pName = new char[strlen(pN) + 1];
if (pName != 0)
{
strcpy(pName, pN) ;
}
}
~Person()
{
cout << "Likvidiruem " << pName << "\n";
pName[0] = '0';
cout << pName << "\n";
delete pName;
cout << pName << "\n";
}

char *pName;
};

void fn() {
Person p1 ("Stroka");
Person p2 = p1;
}

int main(int argcs, char* pArgs[])
{
fn();
int d;
cin >> d;
return 0;
}

[code=cpp]
Вопрос в следующем каким образом указатель pName превратился в массив pName[0] и почему в этом массиве можно присвоить значение только первому элементу , т.е. pName[1] = '0' не работает?

Автор: nikitao 3.12.2005, 20:24
Guest ,у тебя неправельно написано прога.Нельзя так приравнивать обьекты собственного класса в которых используется динамика(Person).Для этого надо перегрузить оператор '='.Дело в том что когда ты приравниваешь(без перегрузки) два обьекта произходит побитовое копирование одного обьекта в другой т е произходит копирование указателя,но не строки smile smile smile .Получается,что два разных указателя в 2 разных обьектах(p1 и p2) указывают на один участок памяти smile
Ошибка возникает в том что в конце ф-ции fn() вызывается 2 деструктора(для p1 и p2),но когда срабатывает деструктор для p2 строка уже удалена деструктором p1(указатели же указывают на один участок).Так что надо доработать.

Надо писать не
Код

delete pName;

а
Код

delete []  pName;

Поскольку это массив


Цитата(Guest @ 3.12.2005, 20:23)
Вопрос в следующем каким образом указатель pName превратился в массив pName[0] и почему в этом массиве можно присвоить значение только первому элементу , т.е. pName[1] = '0' не работает?

Этого я не понял,все работает если написать pName[1]. smile

Автор: Guest 3.12.2005, 21:57
nikitao , да не , я в курсе , просто это немного переделанный пример из книги. Правильно будет вот так :
Код

#include <iostream.h>
#include <string.h>

class Person
{
public:
Person(const Person& p)
{
pName = new char[strlen(p.pName)] ;
if (pName != 0)
{
strcpy(pName, p.pName);
}
}
Person(char *pN)
{
cout << "Sozdayom " << pN << "\n";
pName = new char[strlen(pN) + 1];
if (pName != 0)
{
strcpy(pName, pN) ;
}
}

~Person()
{
cout << "Likvidiruem " << pName << "\n";
pName[0] = '1';
cout << pName << "\n";
delete pName;
cout << pName << "\n";
}

char *pName;
};

void fn() {
Person p1 ("Randy");
Person p2 = p1;
 }

int main(int argcs, char* pArgs[])
{
fn();
int d;
cin >> d;
return 0;
}


Цитата
Этого я не понял,все работает если написать Name[1].


Как ни странно у меня тоже заработало.

Автор: Helicopterr 3.12.2005, 22:11
Вопрос в тему.
Можно ли выделить память опером new для глобального массива? И если да, то где юзать delete[]...

Автор: nikitao 3.12.2005, 22:14
Все равно не верно(но тут уже мелочи:
1.Почему то во 2 конструкторе нужная длина массива высчитывется првельно а в 1-нет(единицу не прибавили)
2.То что перегружен конструктор не перегружает оператор '='.Соответственно вместо
Код

Person p2 = p1;

должно быть
Код

Person p2 ( p1);

3.Ты так и не исправил ошибку с delete(это про '[]')

Добавлено @ 22:18
Helicopterr,да можно,а юзать delete можно где хочешь(можно даже вообще не юзать,но тогда утечка произойдет).Если тебе надо чтоб в самом конце удалялось,то перед
Код

return 0;
}

надо писать.И вообще перед каждым return 0 и exit() в ф-ции main это надо будет прописать.

Автор: S.A.P. 4.12.2005, 01:09
Цитата(nikitao @ 3.12.2005, 22:14)
То что перегружен конструктор не перегружает оператор '='.Соответственно вместо
Person p2 = p1;
Person p2 ( p1);
а тут и не обязательно перегружать '='. Это конструктор копирования и в данном случае коды
Код
Person p2 = p1;
и
Код
Person p2 ( p1);

аналогичны.

Добавлено @ 01:12
Цитата(Helicopterr @ 3.12.2005, 22:11)
Вопрос в тему.
Можно ли выделить память опером new для глобального массива? И если да, то где юзать delete[]...
да.

delete[] юзается где угодно.
Добавлено @ 01:16
Цитата(nikitao @ 3.12.2005, 22:14)
И вообще перед каждым return 0 и exit() в ф-ции main это надо будет прописать.

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

Автор: maxim1000 4.12.2005, 04:31
Цитата
Можно ли выделить память опером new для глобального массива? И если да, то где юзать delete[]...

Цитата
Исключение может составлять вызовы деструкторов у объектов в куче

для нормального вызова деструкторов можно сделать так:
делаем глобальный объект, у него одно поле - указатель
в конструкторе выделяем, в деструкторе освобождаем...

Автор: oberonchik 4.12.2005, 10:31
Я немного вернусь к первому обсуждаемому вопросу в этот посте.
У стека есть ещё одна очень неприятная особенность. Он может медленно расти. Например, был опыт под Linux когда стек рос очень медленно после появления потребности. Т.е. создаются обекты в стеке, а система его наращивает лишь когда появляюся конкретные обращения, причем делает это чуть ли не по 4кб.
Проблема была серьёзная, производительность была низкая, а задача была критична к этому. Под другими никсами и виндой всё было ок. А под целевой системой Linux никак. Долго я этот баг ловил. Потом выделил динмически и всё начало летать

Автор: Helicopterr 4.12.2005, 22:39
однако я читал что поток в стеке быстрее и для небольших массивов, я думаю, в аллокации смысла нет

Автор: maxim1000 4.12.2005, 22:54
обычно (не буду утверждать, что всегда) стек работает быстрее динамической памяти: т.к. он накладывает свои ограничения на порядок создания/удаления объектов - последний созданный удаляется первым
динамическая память не заставляет программиста удовлетворять этому требованию, что приводит у увеличению гибкости и к замедлению выделения/освобождения памяти (из-за различных алгоритмов, связанных с учетом свободного места, которое теперь может быть фрагментированно)
сильно подозреваю, что если при использовании динамической памяти все-таки придерживаться политики стека (последний создан - первый удален), эффективность возрастет (правда, динамической памятью часто пользуются именно для того, чтобы создавать и удалять объекты, когда вздумается)
но даже тогда на большинстве систем стек, скорее всего, будет быстрее, т.к. он также используется при вызове/возврате из функций, а значит, у него больше шансов оказаться в кеше процессора...

Автор: Sellini 20.4.2006, 00:46
Люди помогите,пожалуйста, надо создать 3-х мерный массив в динамической памяти. 

Автор: MAKCim 20.4.2006, 07:35
Цитата

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

1
Код

...
int*** _3d_array=new int** [10];
for (iint i=0; i<10; i++) 
{
    *(_3d_array+i)=new int* [10];
    for (int j=0; j<10; j++) *(*(_3d_array+i)+j)=new int [10];
}
...
// итого массив [10][10][10]

2.
Код

const int M=10, N=10, T=10;
int* _3d_array=new int [M*N*T];
// обращение к элементу [i][j][k] - _3d_array[(M*i+j)*T+k];
 

Автор: threef 20.4.2006, 07:53
По поводу стека: под виндой он тоже выделяется динамически, по мере использования и тоже постранично, по 4 kb.
Можно задавать размеры стека для программ, в которых он является главным хранилищем памяти, например, интенсивно использующим рекурсию. 
При переполнении стека можно поймать исключение и добавить стек, таким образом проблема переполнения стека снимается, как детская болезнь.
Насчет скорости работы в сравнении с кучей - попробую, потом скажу 

Автор: Sellini 21.4.2006, 00:19
Спасибо тебе большое, MAKCim, просто выручил, а то зачет неполучить! 

Автор: Sellini 21.4.2006, 00:37
...
int*** _3d_array=new int** [10];
for (iint i=0; i<10; i++) 
{
    *(_3d_array+i)=new int* [10];
    for (int j=0; j<10; j++) *(*(_3d_array+i)+j)=new int [10];
}
...
А как в этом случае к массиву обращаться? 

Автор: threef 21.4.2006, 14:58
_3d_array[1][3][2]= (int)"hello"; 

Автор: Sellini 22.4.2006, 00:19
Ага, понял, спасибо. 

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