1.51M
Категория: ПрограммированиеПрограммирование

Работа с указателями и динамической памятью (тема 07)

1.

ТАШКЕНТСКИЙ УНИВЕРСИТЕТ ИНФОРМАЦИОННЫХ ТЕХНОЛОГИЙ
ИМЕНИ МУХАММАДА АЛ-ХОРАЗМИЙ
ПРОГРАММИРОВАНИЕ 1
SWD1316
ТЕМА
07
Работа с указателями и
динамической памятью
АБДУЛЛАЕВА ЗАМИРА
ШАМШАДДИНОВНА
Доцент кафедры Основы
информатики
1

2.

Содержание
1. Указатель;
2. Одномерный динамический массив;
3. Двухмерный динамический массив;
4. Заключение.
П РО Г РА М М И РО ВА Н И Е 1
2

3.

Указатели
Адреса констант, переменных, функций и объекты классов в
программе можно хранить в отдельном месте памяти и решать
различные задачи с ними. Переменные, у которых значение является
адресом называются указатели переменных.
Указатели бывают трех видов:
• Какому-то объекту, в частности указатель на переменную;
• Указатель к функции;
• Указатель void.
Указатель должен быть связан какому-то типу, т.е. В указанном адресе
может храниться какая-то значение и должно быть известна сколько
место занимает в памяти это значение.
П РО Г РА М М И РО ВА Н И Е 1
3

4.

Указатели Объявление:
pointer – Указател
char *p; // адрес любого символа или строки
int *pI; // адрес целого числа
float *pF; // адрес действительного числа
Целые переменные и массивы:
int n = 6, A[5] = {0, 1, 2, 3, 4}; int *p; // Указатель к челому числу
p = &n; // запись адреса n
*p = 20; // n = 20
p = A + 2; // запись адреса A[2] (&A[2])
*p = 99; // изменение A[2]
Адрес: 6BCD:000C, значение 3
p ++;
// переход к A[3]
printf(“Адрес: %p, значение %d", p, *p);
П РО Г РА М М И РО ВА Н И Е 1
4

5.

Указатель к объекту
• Если будут объявлены несколько указателей
одного типа, то перед каждым указателем
должно ставиться знак ‘*’ :
int *i, j,*k;
float x,*y,*z;
• В показанном примере объявлены указатели
целого типа i, k и переменная целого типа j, во
втором операторе x – действительная
переменная и y, z – указатель действительного
типа.
П РО Г РА М М И РО ВА Н И Е 1
5

6.

Указатель void
• Этот указатель используется тогда, когда объект типа
неизвестен. Одна из особенностей указателя void – это то
что ему можно прикрепить любой тип. До того как
использовать значение адреса указателя void, надо
прикрепить его к определенному типу. Объявление
указателя void выглядит следующим образом:
void *<название>;
• Сам указатель может быть константой или переменной и
указывать на адрес константы или переменной, например:
П РО Г РА М М И РО ВА Н И Е 1
6

7.

Указатель void
• int i; // целая переменная
• const int ci=1; // целая константа
• int * pi; // указатель к целой переменной
• const int *pci; // указатель к целой
контсанте
• int *const cp=&i;//указатель константа для
целой переменной
• const int*const cpc=&ci; // указатель
константа для целой константы
П РО Г РА М М И РО ВА Н И Е 1
7

8.

Ввод начальных значений указателям
• Указатели часто используются при связи с динамической памяти
(другое название «кучка» или «heap»). Причина называть
память динамической в том, что в этой сфере при процессе
работы программы со свободной памятью выделяется в нужный
момент и при необходимости освобождается.
• Обращение на динамическую память осуществляется при
помощи указателей. Такие переменные называются динамические
переменные и их жизненный цикл начинается от создания до
конца программы или до указанного освобожденного места в
памяти.
П РО Г РА М М И РО ВА Н И Е 1
8

9.

Ввод начальных значений указателям
При объявлении указателей можно задавать ему
начальные значения. Начальное значение (инициализатор)
задается после названия указателя или в скобках или после
знака ‘=’. Начальные значения могут задаваться
следующими методами:
I. Задание адреса объекта к существующиму указателю:
II. Задание абсолютного адреса памяти в открытую:
III. Задание пустого значения:
IV. Выделить место в динамической памяти при
помощи new и присвоить его адрес указателю:
П РО Г РА М М И РО ВА Н И Е 1
9

10.

I. Задание адреса объекта к существующему указателю:
a) При задачи выбора адреса:
int i=5,k=4; // целые переменные
int *p=&i;
// указателю p записывается
адрес
// переменной i
int *p1(&k); // указателю p1 записывается
адрес
// переменной k
b) Задание значения другого
инициализированного указателя:
int * r=p; // p указатель, который ранее
был
// объявлен и имеет значение
П РО Г РА М М И РО ВА Н И Е 1
10

11.

I. Задание адреса объекта к существующему указателю:
с) Задание название массива или функции:
int b[10]; // объявление массива
int *t=b;
// задание начального адреса
массива
void f(int a){/* … */} // определение функции
void (*pf)(int); // объявление указателя к
функции
pf=f; // задание адрес функции указателю
П РО Г РА М М И РО ВА Н И Е 1
11

12.

II. Задание абсолютного адреса памяти в открытую:
char *vp = (char *)0xB8000000;
Здесь 0xB8000000 – 16-ное число
константа и (char*) – это задача
приводящая к типу и он определяет
переход байтов от абсолютног адреса
памяти переменной vp типа char к типу
указателя.
П РО Г РА М М И РО ВА Н И Е 1
12

13.

III. Задание пустого значения:
int *suxx=NULL;
int *r=0;
В первой строке используется особая
константа NULL, во второй строке
использовано значение 0. В обоих случаях
указатель не обращается ни какому объекту.
Пустой указатель обычно используется для
указания указателя к определенному объекту
или определения не существования его.
П РО Г РА М М И РО ВА Н И Е 1
13

14.

IV. Выделить место в динамической памяти при помощи new и
присвоить его адрес указателю :
int * n=new int;
// первый оператор
int * m=new int(10);
// второй оператор
int * q=new int[5]; // третий оператор
В первом операторе при помощи new выделяется
достаточное место int в динамической памяти, его адрес
загружается в указатель n. Место для самого указателя
выделяется при компиляции.
1-operator
2-operator
3-operator
П РО Г РА М М И РО ВА Н И Е 1
14

15.

Задача delete
• Во втором операторе кроме выделения места, размещается начальное
значение число 10 в адрес m.
• В третьем операторе выделяется место для 5 элементов типа int и его
начальный адрес задается указателю q.
• Если память выделяется при помощи new, то он может очистить при
помощи delete. Выше указанных динамических переменных
очищаются от памяти следующим образом:
delete n; delete m; delete[]q;
• Если память выделялось при помощи new[], то его очистка от памяти
осуществляется при помощи delete [] не указывая размерность.
• Не смотря на очистку памяти указатель можно использовать в
дальнейшем.
П РО Г РА М М И РО ВА Н И Е 1
15

16.

Пример
1. #include <iostream>
2. using namespace std;
3. void f(int, int*, int &);
4. int main()
5. {
6. int i=1, j=2, k=3; cout<<i<<" "<<j<<"
"<<k<<endl;
7. f(i, &j, k); cout<<i<<" "<<j<<" "<<k;
8. }
П РО Г РА М М И РО ВА Н И Е 1
16

17.

Продолжение
9. void f(int i, int *j, int &k)
10. {
11. i++;
123
12. (*j)++;
168
13. k++;
14. *j=i+k;
15. k=*j+i; }
П РО Г РА М М И РО ВА Н И Е 1
17

18.

Одномерные динамические массивы
• Динамический массив – это массив, размер которого
заранее не фиксирован и может меняться во время
исполнения программы.
• Для изменения размера динамического массива язык
программирования С++, поддерживающий такие массивы,
предоставляет специальные встроенные функции или
операции.
• Динамические массивы дают возможность более гибкой
работы с данными, так как позволяют не прогнозировать
хранимые объемы данных, а регулировать размер массива
в соответствии с реально необходимыми объемами.
П РО Г РА М М И РО ВА Н И Е 1
18

19.

Объявление
• Под объявлением одномерного динамического массива
понимают объявление указателя на переменную заданного
типа для того, чтобы данную переменную можно было
использовать как динамический массив.
• Синтаксис:
• Тип * Имя_Массива;
• Элементами динамического массива не могут быть функции и
элементы типа void.
• Например:
• int *a;
• double *d;
П РО Г РА М М И РО ВА Н И Е 1
19

20.

Выделение памяти
• Существует 2 способа:
• 1. При помощи операции new
• 2. При помощи библиотечной функции
malloc (calloc)
П РО Г РА М М И РО ВА Н И Е 1
20

21.

Выделение памяти при помощи операции new
• Выделяет для размещения массива участок
динамической памяти соответствующего размера и
не позволяет инициализировать элементы массива.
• Синтаксис:
• Имя_Массива = new Тип [размерность];
Размерность - выражение константного типа;
вычисляется на этапе компиляции.
П РО Г РА М М И РО ВА Н И Е 1
21

22.

продолжение
• /*выделение динамической памяти размером 100*sizeof(int) байтов*/
• int *mas;
• mas = new int [100];
• /*выделение динамической памяти размером n*sizeof(double) байтов*/
• double *m = new double [n];
• /*выделение динамической памяти размером 2*4*sizeof(long) байтов.
При выделении динамической памяти размеры массива должны быть
полностью определены*/
• long (*lm)[4];
• lm = new long [2] [4];
П РО Г РА М М И РО ВА Н И Е 1
22

23.

Выделение памяти при помощи библиотечных функций
malloc (calloc)
• Синтаксис:
• Имя_Массива = (Тип *) malloc(N*sizeof(Тип));
• или
• Имя_Массива = (Тип *) calloc(N, sizeof(Тип));
П РО Г РА М М И РО ВА Н И Е 1
23

24.

продолжение
• float *a;
• a=(float *)malloc(10*sizeof(float));
• // или
• float *a;
• a=(float *)calloc(10,sizeof(float));
• Так как функция malloc (calloc) возвращает
нетипизированный указатель void *, то необходимо
выполнять преобразование полученного нетипизированного
указателя в указатель объявленного типа.
П РО Г РА М М И РО ВА Н И Е 1
24

25.

Освобождение памяти
• Освобождение памяти, выделенной под одномерный динамический
массив, также осуществляется 2 способами.
• 1. При помощи операции delete, которая освобождает участок памяти
ранее выделенной операцией new.
• Синтаксис:
delete [] Имя_Массива;
Например:
• delete [] mas;
• delete [] m;
• delete [] lm;
• Квадратные скобки [] сообщают оператору, что требуется освободить
память, занятую всеми элементами, а не только первым.
П РО Г РА М М И РО ВА Н И Е 1
25

26.

Освобождение памяти
• 2. При помощи библиотечной функции free, которая служит
для освобождения динамической памяти.
• Синтаксис:
free (ИмяМассива);
Имя_Массива – идентификатор массива, то есть
имя указателя для выделяемого блока памяти.
• Например:
• free (a); //освобождение динамической памяти
П РО Г РА М М И РО ВА Н И Е 1
26

27.

Обращение к элементам массива
• Адресация элементов динамического массива осуществляется
аналогично адресация элементов статического массива, то есть с
помощью индексированного имени.
• Синтаксис:
• Имя_Массива[Размерность];
• или
• Имя_Массива[ЗначениеИндекса];
• Например:
• mas[5] – индекс задается как константа,
• sl[i] – индекс задается как переменная,
• array[4*p] – индекс задается как выражение.
П РО Г РА М М И РО ВА Н И Е 1
27

28.

Пример с оператором new
• Сформировать динамический одномерный массив, заполнить его
случайными числами. Преобразовать массив таким образом, чтобы в
первой его половине располагались элементы, стоявшие в чётных
позициях, а во второй половине – элементы, стоявшие в нечётных
позициях.
• #include "stdafx.h"
• #include <iostream>
• using namespace std;
• int main(){
• int *a, n, i;
• //количество элементов
П РО Г РА М М И РО ВА Н И Е 1
28

29.

продолжение
cout << "Sisestage n: ";
cin >> n;
cout << ' '; a = new int [n];
for (i=0; i<n; i++) {
cout << "Sisestage a[" << i << "]: ";
cin >> a[i];
cout << ' ';
}
int *buf = new int [n];
П РО Г РА М М И РО ВА Н И Е 1
29

30.

продолжение int j = 0;
for (i=0; i<n; i+=2) {
buf[j] = a[i];
j++; }
for (i=1; i<n; i+=2) {
buf[j] = a[i];
j++; }
cout <<"Uus : " << ' ';
for (i=0; i<n; i++)
cout << buf[i] << ' ';
delete [] a; delete [] buf;
system("pause");
return 0;
}
П РО Г РА М М И РО ВА Н И Е 1
30

31.

Пример с функциями malloc (calloc)
• Указатель на массив не обязательно должен показывать на
начальный элемент некоторого массива. Он может быть сдвинут
так, что начальный элемент будет иметь индекс, отличный от нуля,
причем он может быть как положительным, так и отрицательным.
• int main()
•{
• float *mas;
• int m;
• scanf("%d",&m);
• mas=(float *)calloc(m,sizeof(float));
• mas[0]=22.3;
П РО Г РА М М И РО ВА Н И Е 1
31

32.

продолжение
mas-=5;
mas[5]=1.5;
mas[6]=2.5;
mas[7]=3.5;
mas+=5;
mas+=2;
mas[-2]=8.2;
mas[-1]=4.5;
mas-=2;
mas--;
free(++mas);
system("pause");
return 0;
}
П РО Г РА М М И РО ВА Н И Е 1
32

33.

Двумерные динамические массивы
Двумерный массив - это одномерный массив, элементами которого
являются одномерные массивы. Динамическим массивом
называют массив с переменным размером, то есть количество
элементов может изменяться во время выполнения программы.
Для создания двумерного динамического массива вначале нужно
распределить память для массива указателей на одномерные
массивы, а затем выделить память для одномерных массивов.
При динамическом распределении памяти для массивов следует
описать соответствующий указатель, которому будет присвоено
значение адреса начала области выделенной памяти.
П РО Г РА М М И РО ВА Н И Е 1
33

34.

Объявление двумерных динамических массивов
Под объявлением двумерного динамического массива
понимают объявление двойного указателя, то есть
объявление указателя на указатель.
Синтаксис:
Тип ** Имя_Массива;
Элементами динамического массива не могут быть функции
и элементы типа void.
Например:
int **a;
float **m;
П РО Г РА М М И РО ВА Н И Е 1
34

35.

Выделение памяти
При формировании двумерного динамического массива сначала
выделяется память для массива указателей на одномерные
массивы, а затем в цикле с параметром выделяется память под
одномерные массивы.
П РО Г РА М М И РО ВА Н И Е 1
35

36.

Выделение памяти
При работе с динамической памятью в языке С++
существует 2 способа выделения памяти под
двумерный динамический массив.
1. при помощи операции new, которая позволяет
выделить в динамической памяти участок для
размещения массива соответствующего типа, но не
позволяет его инициализировать.
2. при помощи библиотечной функции malloc
(calloc), которая предназначена для выделения
динамической памяти.
П РО Г РА М М И РО ВА Н И Е 1
36

37.

при помощи операции new
Синтаксис выделения памяти под массив указателей:
Имя_Массива = new Тип * [Размерность];
Синтаксис выделения памяти для массива значений:
Имя_Массива[ЗначениеИндекса]=newТип[Размерность];
Имя_Массива – идентификатор массива, то есть имя
двойного указателя для выделяемого блока памяти.
Тип – тип указателя на массив.
Размерность – задает количество элементов массива.
Это выражение константного типа и
вычисляется на этапе компиляции.
П РО Г РА М М И РО ВА Н И Е 1
37

38.

Пример
int n, m;
float **matr;
matr = new float * [n];
for (int i=0; i<n; i++)
matr[i] = new float [m];
NB!!! При выделении динамической памяти размеры
массивов должны быть полностью определены.
П РО Г РА М М И РО ВА Н И Е 1
38

39.

при помощи malloc (calloc)
Синтаксис выделения памяти под массив указателей:
Имя_Массива = (Тип **) malloc(N*sizeof(Тип *));
или
Имя_Массива = (Тип **) calloc(N, sizeof(Тип *));
Синтаксис выделения памяти для массива значений:
Имя_Массива[ЗначениеИндекса]=
(Тип*)malloc(M*sizeof(Тип));
или
Имя_Массива[ЗначениеИндекса]=
(Тип*)calloc(M,sizeof(Тип));
П РО Г РА М М И РО ВА Н И Е 1
39

40.

Пример
Например:
int n, m;
float **matr;
matr = (float **) malloc(n*sizeof(float *));
for (int i=0; i<n; i++)
matr[i] = (float *) malloc(m*sizeof(float));
NB!!! Т.к. функция malloc (calloc) возвращает
нетипизированный указатель void *, то необходимо
выполнять его преобразование в указатель объявленного
типа.
П РО Г РА М М И РО ВА Н И Е 1
40

41.

Освобождение памяти
Удаление из динамической памяти двумерного
массива осуществляется в порядке, обратном его
созданию:
сначала освобождается память, выделенная под
одномерные массивы с данными,
затем освобождается память, выделенная под
одномерные массив указателей.
П РО Г РА М М И РО ВА Н И Е 1
41

42.

Освобождение памяти
Освобождение памяти, выделенной под двумерный
динамический массив, также осуществляется 2 способами:
при помощи операции
, которая освобождает
участок памяти ранее выделенной операцией new.
при помощи библиотечной функции
, которая
предназначена для освобождения динамической памяти.
П РО Г РА М М И РО ВА Н И Е 1
42

43.

При помощи операции delete
Синтаксис освобождения памяти, выделенной для массива
значений:
delete Имя_Массива [ЗначениеИндекса];
Синтаксис освобождения памяти, выделенной под массив
указателей:
delete [] Имя_Массива;
Имя_Массива – идентификатор массива, то есть имя
двойного указателя для выделяемого блока памяти.
П РО Г РА М М И РО ВА Н И Е 1
43

44.

Пример
//освобождает память, выделенную для массива значений
for (int i=0; i<n; i++)delete matr [i];
//освобождает память, выделенную под массив указателей
delete [] matr;
П РО Г РА М М И РО ВА Н И Е 1
44

45.

При помощи функции free
Синтаксис освобождения памяти, выделенной для массива значений:
free (Имя_Массива[ЗначениеИндекса]);
Синтаксис освобождения памяти, выделенной под массив
указателей:
free (Имя_Массива);
ИмяМассива – идентификатор массива, то есть имя двойного
указателя для выделяемого блока памяти.
Пример:
for (int i=0; i<n; i++)
free (matr[i]);
free (matr);
П РО Г РА М М И РО ВА Н И Е 1
45

46.

Обращение к элементам
Адресация элементов динамического массива осуществляется с
помощью индексированного имени.
Синтаксис:
или
Например:
mas[5][7] – индекс задается как константа,
А[i][j] – индекс задается как переменная,
array[4*p][p+5] – индекс задается как выражение.
П РО Г РА М М И РО ВА Н И Е 1
46

47.

Пример
#include <iostream>
using namespace std;
int main()
{ int n,i,j;
int **matr;
cout << "Input matrix order:";
cin >> n;
matr = new int *[n];
П РО Г РА М М И РО ВА Н И Е 1
47

48.

продолжение
for(i=0; i<n; i++)
{ matr[i] = new int[n];
for (j=0; j<n; j++)
matr[i][j] = (i==j ? 1 : 0); }
cout << "Result: ";
for(i=0; i<n; i++)
{ cout << "\n";
for (j=0; j<n; j++)
cout << " " << matr[i][j];
delete matr[i]; }
delete [] matr;
return 0;
}
П РО Г РА М М И РО ВА Н И Е 1
48

49.

Заключение
Обычно, объем памяти, необходимый для той или иной
переменной, задается еще до процесса компиляции посредством
объявления этой переменной. Если же возникает необходимость в
создание переменной, размер которой неизвестен заранее, то
используют динамическую память.
Резервирование и освобождение памяти в программах на C++
может происходить в любой момент времени.
П РО Г РА М М И РО ВА Н И Е 1
49

50.

ТАШКЕНТСКИЙ УНИВЕРСИТЕТ ИНФОРМАЦИОННЫХ ТЕХНОЛОГИЙ
ИМЕНИ МУХАММАДА АЛ-ХОРАЗМИЙ
СПАСИБО ЗА ВНИМАНИЕ!
АБДУЛЛАЕВА ЗАМИРА
ШАМШАДДИНОВНА
Доцент кафедры Основы
информатики
50
English     Русский Правила