Глава 9. Динамические переменные и массивы
Динамические переменные
Одномерные динамические массивы
Двумерные динамические массивы
Переменные, массивы и другие данные, находящиеся в фиксированных областях памяти, называются статическими. Память для них резервируется во время компиляции программы.
В языке С++ существует и другой способ выделения памяти для данных – во время выполнения программы. Такие данные (переменные, массивы) называются динамическими, они создаются по требованию программы и запоминаются в блоках памяти переменного размера, принадлежащих специальным образом организованной области памяти – так называемой ”куче”.
Динамические переменные
В С++ наиболее распространённым способом создания динамической переменной является использование операции new и указателя:
переменная-указатель = new тип_переменной;
Операция new выделяет область памяти в куче для хранения значения заданного типа и возвращает адрес начала выделенной памяти, который присваивается переменной-указателю, например:
int * p = new int;
При выполнении этого оператора создаётся динамическая переменная целого типа, адрес которой присваивается указателю p.
С помощью операции new можно выделить память в куче для значений переменных любого допустимого типа.
Если свободной памяти в куче для создания новой переменной оказалось недостаточно, операция new возвращает нулевое значение (0 или NULL). Поэтому программа должна проверять успешность создания динамической переменной:
if(p == NULL){ // или if(!p)
cout<<”Error: no memory”<<endl;
exit(1);
}
При создании динамической переменной допускается сразу присвоить ей значение (проинициализировать):
int * p = new int(5);
В результате выполнения данного оператора переменной, адресуемой указателем p, будет присвоено значение 5.
После выделения области памяти и присваивания её адреса указателю можно разыменовать этот указатель и использовать память, как если бы она принадлежала статической переменной.
После того как необходимость в использовании динамической переменной отпала, занимаемую её память следует освободить. Это можно сделать с помощью операции delete:
delete переменная-указатель;
Операция delete не требует указывать размер освобождаемой памяти, так как в С++ существует специальный механизм, позволяющий автоматически связывать с указателем размер области памяти, на которую он указывает.
Пример. Использование операций new, delete.
…
int *p = new int; // выделение памяти для int- значения
*p = 20; // занесение в эту память значения 20
cout<<*p<<endl; // вывод 20 на экран
delete p; // освобождение памяти
…
Переменные, созданные с помощью операции new, называются динамическими переменными, потому что они создаются и уничтожаются в процессе выполнения программы. Динамические переменные размещаются в куче – динамической памяти. Для доступа к таким переменным используются только указатели (косвенная адресация), так как ячейкам динамической памяти нельзя присвоить имя. Занимаемая переменной память в куче освобождается с помощью операции delete.
Одномерные динамические массивы
Как мы уже знаем, элементы одномерного массива располагаются в памяти компьютера подряд, начиная с адреса, соответствующего началу массива. При традиционном объявлении массива компилятору должны быть чётко указаны имя массива, размер массива и тип элементов, который явно определяет размеры памяти, выделяемой под каждый элемент массива, т.е. размер памяти, выделяемый для массива, полностью и однозначно определен.
Это не всегда удобно. Иногда нужно, чтобы память для массива выделялась в таких размерах, какие нужны для решения конкретной задачи, причем размеры памяти заранее не известны.
Решить указанную проблему можно с помощью формирования массивов с переменными размерами или динамических массивов. Операция new позволяет создавать динамический массив, а операция delete освобождает ранее выделенную динамическую память.
В соответствии с синтаксисом операция new при использовании с одномерным динамическим массивом имеет следующий формат:
переменная-указатель имя_массива = new тип_массива [размер];
Например, в результате выполнения оператора
int *p = new int[6];
объявляется переменная-указатель p, а затем указателю присваивается адрес выделенной области памяти для 6 элементов массива в соответствии с заданным типом массива. Если с помощью операции new невозможно выделить требуемый объём памяти, то результатом операции new является нулевое значение (0 или NULL).
Динамически созданный массив может быть удален, когда работа с ним завершена. Для освобождения памяти, выделенной для динамического одномерного массива, используют операцию delete, которая имеет следующий формат:
delete [] имя_массива;
При динамическом выделении памяти для массива следует помнить, что его нельзя одновременно и инициализировать. Размер для динамического массива может быть задан во время выполнения программы. Доступ к элементам массива возможен только через указатель – p[i], *(p + i).
Пример. Динамическое выделение и освобождение памяти для одномерного массива. Размер массива вводится с клавиатуры.
…
int n;
cin>>n; // n – размер массива
int* mas = new int[n]; // выделение памяти
if(!mas){cout<<"No memory"<<endl; exit(1);}
for(int i = 0; i < 6; i++ // присваивание элементам
p[i] = i + 1; ... // массива значений 1 2 3 4 5 6
delete [] mas; // освобождение памяти
Следует отметить, что оператор
int * p = new int[n];
можно разделить на два оператора
int *p; p = new int[n];
Существенным отличием динамического массива от обычного является возможность удаления его в процессе выполнения программы.
// Пример 9.1. Память для динамического массива выделяется
// в main(). Динамический массив создаётся в функции vvod()
// случайной выборкой. Выводится массив в функции vivod().
// В функции sum() вычисляется сумма элементов массива.
#include <iostream>
using namespace std;
void vvod(int*, int);
void vivod(int*, int);
int sum(int* a, int kol);
int main(){
int n;
cout<<"Vvedi kol-vo elementov massiva : "; cin>>n;
int* p = new int[n];
if(!p){cout<<"No memory"<<endl; exit(1);}
vvod(p, n);
cout<<"\n\tmain: Isxodni massiv =="<<endl;
vivod(p, n);
int y = sum(p, n);
cout<<"\n\tSumma = "<<y<<endl;
delete [] p;
system("pause");
}
void vvod(int* p, int n){
srand(n);
for(int i = 0;i < n; i++){
p[i] = rand() % 15 - rand() % 10;
}
}
void vivod(int* p, int n){
for(int i = 0; i < n; i++)
cout<<p[i]<<' '; // cout<<*(p+i)<<' ';
cout<<endl;
}
int sum(int* a, int kol){ // функция sum() вычисления суммы
int s = 0; // элементов динамического массива
for(int i = 0; i < kol; i++){
s = s + a[i];
}
return s;
}
// Пример 9.2. Память выделяется и сам массив создаётся в функции
// vvod(). Функция vvod()возвращает указатель на массив, который
// передаётся в функцию vivod(). Используется меню.
#include <iostream>
using namespace std;
void vivod(int *, int);
int* vvod(int);
int main(){
int n; int *p;
int vibor;
cout<<"vvedi kol-vo elementov massiva: "; cin>>n;
while(1){
cout<<"\n vvedite nomer punkta:\n";
cout<<"vvod---->1"<<endl
<<"vivod--->2"<<endl
<<"exit----->3"<<endl;
cin>>vibor;
switch(vibor){
case 1: system("cls"); p = vvod(n); break;
case 2: system("cls"); vivod(p, n); break;
case 3: system("cls");
cout<<"Good-Bye!"; exit(1); break;
}
}
cout<<endl;
delete [] p;
system("pause");
}
int* vvod(int n){
int *p = new int[n];
if(!p){cout<<"No memory"<<endl; exit(1);}
for(int i = 0; i < n; i++){
cout<<"vvedi a:["<<i<<"] ";
cin>>*(p + i);
}
return p;
}
void vivod(int *p, int n){
for(int i = 0; i < n; i++)
cout<<*(p + i )<<' '; // cout<<p[i]<<' ';
cout<<endl;
}