
Общие сведения
Все глобальные переменные и константы, объявленные в программе на языке C++, размещаются в одной непрерывной области оперативной памяти, которая называется сегментом данных [6]. Длина сегмента данных определяется архитектурой процессора 8086 и составляет 64 Кбайта, что может вызвать определенные затруднения при обработке больших массивов данных. С другой стороны, объем памяти ПЭВМ достаточен для успешного решения задач с большой размерностью данных. Выходом из положения может служить использование так называемой динамической памяти.
Динамическая память - это оперативная память ПЭВМ, представляемая си-программе при её работе, за вычетом сегмента данных, стека, и собственно тела программы. Размер динамической памяти может варьировать в широких пределах. Динамическая память - это фактически единственная возможность обработки массивов данных большой размерности. Существуют и другие задачи, которые трудно или невозможно решить без использования динамической памяти. В частности, такие проблемы возникают при разработке систем автоматизированного проектирования: размерность математических моделей может значительно отличаться в различных проектах, статическое распределение памяти в этом случае практически невозможно. Кроме того, динамическая память используется для временного запоминания данных при работе с графическими и звуковыми средствами ПЭВМ.
Д
82
Для управления динамической памятью в Си-программах используются уже знакомые нам указатели. Вся динамическая память рассматривается как подобная стеку структура, называемая "кучей".
Для выделения памяти под любую переменную используется операция new [3]:
указатель = new имя_инициализатор
Эта операция позволяет выделить и сделать доступным свободный участок в основной памяти, размеры которого соответствуют типу данных, определяемому именем типа. B выделенный участок памяти заносится значение, определяемое инициализатором, который не является обязательным элементом. В случае успешного выполнения операция new возвращает адрес начала выделенного участка, памяти. Если участок нужных размеров не может быть выделен (нет памяти), то операция new возвращает нулевое значение указателя (NULL). Необязательный параметр инициализатор - это выражение в круглых скобках. Указатель должен ссылаться на тот же тип, что имя_типа в операции new. Например, можно написать такой фрагмент программы:
int *ptr; float *ptr1;
ptr=new int;
*ptr=10; ptr1=new float(13.15);
В первом случае операция new позволяет выделить 2 байта памяти и адрес начала выделенного участка присваивается указателю ptr. Следующий оператор присваивания инициализирует этот участок памяти числом 10. Во втором случае операция new позволяет выделить 4 байта-памяти, адрес начала выделенного участка присваивается указателю ptr1 и этот участок памяти инициализируется числом 13.15. В дальнейшем доступ к выделенному участку памяти осуществляется с помощью операции косвенной адресации (или разыменовывание). Продолжительность существования выделенного участка - от точки создания до конца программы или до явного освобождения. С помощью операции
delete указатель осуществляется явное освобождение памяти. Например:
delete ptr;
delete ptr1;
Если указатель, на который действует операция delete, не содержит адрес блока, зарезервированного ранее операцией new, то последствия будут непредсказуемыми.
Д
83
int *mas1; // Одномерный массив
mas1=new int[n];
float *mas2; // Двумерный массив
mas2=new float[n*n];
При этом к элементам одномерного массива можно обращаться по индексам и по указателю с помощью косвенной адресации, а к элементам – только по указателю. Чтобы работать с индексами в двумерном массиве, необходимо явным образом указать размерность массива.
Рассмотрим пример выделения и освобождения памяти для массивов. Пусть дана матрица MAS2[N,N] и одномерный массив MAS1[N]. Требуется заменить элементы главной диагонали матрицы элементами одномерного массива. Матрицу формируем с помощью датчика случайных чисел, а одномерный массив введем с клавиатуры.
// Пример pr25
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
void main()
{
int n;
printf("\nВведите количество элементов в массиве");
scanf("%d",&n);
int *mas1, *ptr,i;
mas1=new int[n]; // Выделение памяти под одномерный массив
int *mas2=new int[n*n];//Выделение памяти под двумерный массив
randomize();
for(i=0; ptr=mas2; i<n*n; i++, ptr++)
*ptr=random(25); // Формирование двумерного массива
printf("\nВведите элементы одномерного массива");
for (i=0; i<n; i++)
scanf("%d", mas1[i]);
// Вывод элементов одномерного массива
for (i=0; i<n; i++)
printf("%d"\t, mas1[i]);
puts("\nВывод элементов двумерного массива");
for(i=0,ptr=mas2; i<n*n; i++,ptr++)
{ if(i%n==0) // Перевод курсора, если новая строка
printf("\n");
printf("%d\t",*ptr);
}
// Замена элементов главной диагонали матрицы элементами
// одномерного массива
84
*ptr=mas1[i]; puts("Обработанный массив");
for(i=0,ptr=mas2; i<n*n; i++,ptr++)
{ if(i%n==0)
printf("\n"); printf("%d\t",*ptr);
}
// Освобождение памяти
delete [ ] mas 1;
delete [ ] mas2;
getch();
}
В предыдущей программе двумерный массив в итоге был обработан как одномерный массив. Если все-таки необходимо воспользоваться индексами в двумерном массиве, то нужно вспомнить, что имя одномерного массиве является указателем на первый элемент массиве. Таким образом, двумерный массив можно рассматривать как одномерный массив указателей. При этом усложняется выделение и освобождение динамической памяти. В следующей программе формируется с помощью датчика случайных чисел в динамической памяти двумерный массив, а из сумм элементов строк - одномерный массив. Главным преимуществом этой и предыдущей программы является возможность вводить с клавиатуры размеры массива.
// Пример рr26
#include<stdio.h>
#include<stdlib.h>
#include <conio.h>
void main()
{
int n,m;
printf("\nВведите количество строк и столбцов в матрице");
scanf("%d%d",&n,&m);
int *mas,j,i;
mas=new int[n]; // Выделение памятей для одномерного массива
int **mas2; // Указатель для массива указателей - матрица
mas2=new int *[n]; // Выделение памяти для массива указателей
randomize();
for(i=0; i<n; i++)
{
mas2[i]=new int[m]; // Выделение памяти под строку матрицы
for(j=0; j<m; j++)
mas2[i][j]=random(25);
}
p
85
{
printf("\n");
for(j=0; j<m; j++)
printf("%d\t",mas2[i][j]);
}
// Формирование одномерного массива
for(i=0; i<n; i++)
{
mas[i]=0;
for(j=0; j<m; j++)
mas[i]=mas[i]+mas2[i][j];
}
printf("\n");
puts("Вывод элементов одномерного массива");
for(i=0; i<n; i++)
printf("%d\t",mas[i]);
// Освобождение памяти
for(i=0; i<n; i++)
delete mas2[i];
delete [ ]mas2;
delete [ ] mas; getch();
}
Контрольные вопросы
1. Зачем нужны динамические массивы?
2. Как описываются динамические массивы?
3. Как организовать работу с указателями в двумерном массиве?
4. Как объяснить формулу pr25: ptr=ptr+n+1?
5. Составьте программу, которая, используя динамические массивы, в заданной квадратной целочисленной матрице определяет количество простых чисел.
Варианты заданий
Варианты заданий выбираются из заданий к лабораторной работе №5 по следующему правилу: если у вас i-й вариант, то выбираете задачу с номером 26 - i.
86
ЛАБОРАТОРНАЯ РАБОТА №7
ФУНКЦИИ
Цель работы: Написание и отладка программ с использованием, функций.
Порядок выполнения работы:
В соответствии с поставленной задачей необходимо разработать графическую схему алгоритма, составить программы.
К данной работе предъявляются следующие требования: левую часть уравнения нужно оформить как функцию C++, в главной функций обеспечить ввод исходных данных, реализацию метода решения уравнения и вывод результата на экран.