Вы находитесь на странице: 1из 5

Лабораторная работа № 6

Динамические переменные в языке программирования C

Цель и задача работы: научиться использовать динамические переменные. В


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

Теоретические положения

Использование статических объектов (объекты, порождаемые непосредственно


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

Указатели в С

Указатель – это адрес памяти, распределяемой для размещения идентификатора


(в качестве идентификатора может выступать имя переменной, массива, структуры,
строкового литерала). В том случае, если переменная объявлена как указатель, то она
содержит адрес памяти, по которому может находиться скалярная величина любого
типа. При объявлении переменной типа указатель необходимо определить тип
объекта данных, адрес которых будет содержать переменная, и имя указателя с
предшествующей звездочкой (или группой звездочек). Формат объявления указателя:
спецификатор-типа [ модификатор ] * описатель
Спецификатор-типа задает тип объекта и может быть любого основного типа,
типа структуры, смеси (об этом будет сказано ниже). Задавая вместо спецификатора-
типа ключевое слово void, можно своеобразным образом отсрочить спецификацию
типа, на который ссылается указатель. Переменная, объявляемая как указатель на тип
void, может быть использована для ссылки на объект любого типа. Однако для того,

1
чтобы можно было выполнить арифметические и логические операции над
указателями или над объектами, на которые они указывают, необходимо при
выполнении каждой операции явно определить тип объектов. Такое определение
типов может быть выполнено с помощью операции приведения типов.
В качестве модификаторов при объявлении указателя могут выступать
ключевые слова const, near, far, huge. Ключевое слово const указывает, что указатель
не может быть изменен в программе. Размер переменной, объявленной как указатель,
зависит от архитектуры компьютера и от используемой модели памяти, для которой
будет компилироваться программа. Указатели на различные типы данных не
обязательно должны иметь одинаковую длину.
Для модификации размера указателя можно использовать ключевые слова near,
far, huge.
Примеры:
unsigned int * a; /* переменная а представляет собой указатель
на тип unsigned int (целые числа без знака) */
double * x; /* переменная х указывает на тип данных с
плавающей точкой удвоенной точности */
char * fuffer ; /* объявляется указатель с именем fuffer
который указывает на переменную типа char */
double nomer;
void *addres;
addres = & nomer;
(double *)addres ++;
/*
Переменная addres объявлена как указатель на объект любого типа. Поэтому ей
можно присвоить адрес любого объекта (& - операция вычисления адреса). Однако,
как было отмечено выше, ни одна арифмитическая операция не может быть выполнена
над указателем, пока не будет явно определен тип данных, на которые он
указывает. Это можно сделать, используя операцию приведения типа (double *) для
преобразования addres к указателю на тип double, а затем увеличение адреса.
*/

const char * dr;


/*
Переменная dr объявлена как указатель на константное выражение, т.е. значение
указателя может изменяться в процессе выполнения программы, а величина, на
которую он указывает, нет.
*/

unsigned char * const w = &obj.


/*
Переменная w объявлена как константный указатель на данные типа char unsigned.
Это означает, что на протяжении всей программы w будет указывать на одну и ту же
область памяти. Содержание же этой области может быть изменено.
*/

2
Работа с памятью в С

Функции для работы с памятью располагаются в библиотеках С: Dos.h, Alloc.h,


Stdlib.h, Malloc.h.
Описание основных функций для работы с памятью:
 calloc (stdlib.h, alloc.h) предоставляет доступ к куче (heap), которая
доступна для динамического распределения по произвольным блокам памяти.
Многие структуры данных (например, деревья и списки) используют кучу (heap) для
размещения. Функция размещает в памяти блок соответствующего размера и
возвращает указатель на созданный блок. Максимальный размер блока – 64Кб. Для
блоков большей размерности используется функция farcalloc.
 free (stdlib.h, alloc.h) освобождает блок памяти, выделенный ранее с
помощью функции calloc. Для функции farcalloс – соответственно farfree.
 allocmem (dos.h) использует вызов DOS для выделения блока свободной
памяти и возвращает адрес начала сегмента выделенного блока памяти.
 freemem (dos.h) освобождает блок памяти, выделенный с помощью
allocmem.
 alloca (malloc.h) выделяет некоторый объем памяти в стеке. Стек
автоматически освобождается после завершения работы вызывающей функции.
Другие функции, описанные в указанных библиотеках, по сути аналогичны
вышеописанным за исключением некоторых особенностей использования.

Использование динамических переменных в С++

В языке программирования С++ для динамического распределения памяти


существуют операции new и delete. Операция
new имя_типа или new имя_типа (инициализатор)
позволяет выделить и сделать доступным свободный участок в основной памяти,
размеры которого соответствуют типу данных, определяемому именем типа. В
выделенный участок заносится значение, определяемое инициализатором, который не
является обязательным элементом. В случае успешного выполнения new возвращает
адрес начала выделенного участка памяти. Если участок нужных размеров не может
быть выделен (нет памяти), то операция new возвращает нулевое значение адреса
(NULL). Синтаксис применения операции:
указатель = new имя_типа (инициализатор);
Операция new float выделяет участок памяти размером 4 байта. Операция new
int(15) выделяет участок памяти 2 байта и инициализирует этот участок целым
3
значением 15. Синтаксис использования операций new и delete предполагает
применение указателей. Предварительно каждый указатель должен быть определен:
тип *имя_указателя;
В качестве типа можно использовать, например, стандартные типы int, long,
float, double, char.
В качестве имени типа в операции new может быть использован массив:
new тип_массива
При выделении динамической памяти для массива его размеры должны быть
полностью определены.
ptr = new int [10]; /* 10 элементов типа int, или 20 байт */
ptr = new int [ ]; /* неверно, т.к. не определен размер */

Такая операция позволяет выделить в динамической памяти участок для


размещения массива соответствующего типа, но не позволяет его инициализировать.
В результате выполнения операция new возвратит указатель, значением которого
служит адрес первого элемента массива.
Для освобождения выделенного операцией new участка памяти используется
оператор:
delete указатель;
Указатель адресует освобождаемый участок памяти, ранее выделенный с
помощью операции new. Для освобождения памяти, выделенной для массива,
используется следующая модификация того же оператора:
delete [ ] указатель;

Динамическое размещение массивов

При динамическом распределении памяти для массивов следует описать


соответствующий указатель и присваивать ему значение, например, при помощи
функции calloc(). Одномерный массив a[10] из элементов типа float можно создать
следующим образом :
float *a;
a=(float*)(calloc(10,sizeof(float));

Для создания двумерного массива вначале нужно распределить память для


массива указателей на одномерные массивы, а затем распределять память для
одномерных массивов. Пусть, например, требуется создать массив a[m][n], это можно
сделать при помощи следующего фрагмента программы:
main ()
{
4
double **a;
int m,n,i;
scanf("%d %d",&m,&n);
a=(double **)calloc(m,sizeof(double *));
for (i=0; i<m; i++)
a[i]=(double *)calloc(n,sizeof(double));
. . . . . . . . . . . .
}
Аналогичным образом можно распределить память и для трехмерного массива
размером m,n,l. Следует только помнить, что ненужную для дальнейшего выполнения
программы память следует освобождать при помощи функции free().
main ()
{
long ***a;
int m,n,l,i,j;
scanf("%d %d %d",&m,&n,&l);
/* -------- распределение памяти -------- */
a=(long ***)calloc(m,sizeof(long **));
for (i=0; i<m; i++)
{ a[i]=(long **)calloc(n,sizeof(long *));
for (j=0; j<n; j++)
a[i][j]=(long *)calloc(l,sizeof(long));
}
. . . . . . . . . . . .
/* --------- освобождение памяти ----------*/
for (i=0; i<m; i++)
{
for (j=0; j<n; j++)
free (a[i][j]);
free (a[i]);
}
free (a);
}

При работе с динамической памятью в стиле С++ нужно использовать


операторы new и delete для выделения и освобождения памяти соответственно.

Задание

Выполните задание по работе с двумерными массивами из лабораторной


работы № 3 «Работа с массивами в языке программирования C», взяв в качестве
номера варианта назначенный преподавателем, и используя динамическое создание
массивов. Размер массива должен задаваться пользователем с клавиатуры. Результат
работы программы должен выводиться на экран и в файл.