Similar presentations:
Информатика_5_2025
1. Глава 5. Использование динамической памяти 5.1 Адресация оперативной памяти.
Минимальная адресуемая единица памяти большинствасовременных процессоров – байт.
Байты памяти нумеруют, начиная с нуля. Номер байта и есть его
адрес.
1 2 3 4
Физический адрес
Аф – номер байта
оперативной памяти.
Адрес первого
(младшего)
байта.
Объем занимаемой
памяти, т.е. количество
байтов в памяти.
Размещаемая в памяти компьютера информация: числовые и
текстовые данные, а также машинные коды программы - обычно
имеют размер более 1 байта, т.е. занимают в памяти некоторое
количество байтов.
Адрес программного объекта (переменной, подпрограммы и т.п.) =
адрес его первого (младшего) байта.
1
2. Непрерывное и дискретное распределение памяти
Программаили данные
ПП
АА
ММ
ЯЯ
ТТ
ЬЬ
11
21
2
32
3
43
4
4
Программа
или данные
Таблица описаний
свободных и занятых
блоков
3
Программа
или данные
Проблема
– фрагментация
памяти,
Проблема
– фрагментация
памяти,
при
которой
суммарный
свободный
при
которой
суммарный
свободный
объем
памяти
может
быть
достаточен
объем
памяти
может
быть
для
размещения
а
достаточен
дляданных,
размещения
непрерывного
куска нужного
размера
данных, а непрерывного
куска
нет…
нужного размера нет…
1
2
4
Проблемы:
– часть памяти занята таблицей и
возможны неполные блоки;
– фрагментация памяти, при которой
части информации могут быть
расположены в разных местах
памяти…
ПП
АА
ММ
ЯЯ
ТТ
ЬЬ
3. 5.2 Указатели и операции над ними
Указатель – тип данных, используемый при разработке программы дляхранения адресов. В памяти для 32-х разрядного приложения указатель
занимает 4 байта и адресует сегмент размером V = 232= 4Гб.
При сегментной модели памяти адреса определяются относительно
начала сегмента.
[<И1>][<Тип данных>][<Тип>] [И2]*<Имя>[=<Значение>];
Где :
<И1> - признак изменчивости содержимого по адресу указателя. Задается
ключевым словом const. При этом значение содержимого памяти,
которую адресует указатель, нельзя менять. Может отсутствовать.
<И2> - признак изменчивости указателя. Задается ключевым словом
const. При этом значение самого указателя нельзя менять.
Может отсутствовать.
<Тип данных> - тип данных, адресуемых указателем. Любой тип,
определенный в С++, в том числе void.
<Тип > - тип указателя. Определяется моделью памяти. Может быть far
3
или near. Если тип указателя не указан, принимается near.
4. Примеры определения указателей
ptrs1) short a, *ptrs =&a;
a
Указатель можно
инициализировать адресом
реальной переменной
2) const short *ptrs;
a
Неизменяемое значение:
можно ptrs = &b; нельзя
*ptrs=10;
3) short *const ptrs=&a;
Неизменяемый указатель
можно *ptrs=10; нельзя ptrs = &b;
4
5. 5.2.1 Типизированные и нетипизированные указатели
Различают указатели:• типизированные – адресующие данные конкретного типа;
• нетипизированные – не связанные с данными определенного типа.
Объявление типизированного указателя:
int *b,*c ;
float *s ,double *f;
long double *l;
Все указатели несут в
себе сведения о размере
памяти, адресуемой этим
указателем
Объявление нетипизированного указателя:
void * <имя>;
Этот указатель создан как бы «на все случаи жизни».
Он отличается от других отсутствием сведений о размере
соответствующего участка памяти. Поэтому его легко связывать с
указателями других типов.
5
6. Нулевой указатель
В С определена адресная константа NULL;С++ определены адресная константа nullptr;
Эти константы определяют адрес, который никуда не
указывает или «нулевой указатель».
Его можно присвоить указателю любого типа.
Например:
0
pi
int *pi=NULL; // int *pi=nullptr;
0
pf
float *pf=NULL; // int *pi=nullptr;
0
b
void *b=NULL; // int *pi=nullptr;
Кроме того, этb константs можно использовать в
операциях сравнения при проверке логических
адресных выражениях.
while (pi!=NULL) { …};
6
7. 5.2.2 Операции над указателями
1.Присваивание.
Допускается присваивать указателю значение другого указателя того же типа
или нулевого указателя.
Пример:
Типизированному
int *p1,*p2;
указателю нельзя
float *p3,*p4;
присваивать
нетипизированный и
void *p;...
наоборот!
{допустимые операции}
p1=p2; p4=p3; p1=NULL; // нулевой адрес C
p=nullptr; // нулевой адрес (начиная с C++11) ...
{недопустимые операции}
p3=p2; p1=p3; p3=p; p=p4;
Однако, при необходимости выполнить операцию присваивания, можно
использовать явное переопределение типа, для приведения указателя
одного типа к другому.
p3=(float*)p2;
p2=(int*)p3;
p1=(int*)p;
Явное переопределение
типа указателя
7
8. Операции над указателями (2)
2. Получение адреса (&).Результат операции – адрес некоторой области памяти, который
можно присвоить указателю.
Это можно сделать:
a) При помощи операции присваивания:
int *pi,i=10;
pi
... pi=&i;
i
10
b) Во время инициализации указателя при его
b
определении:
5.7
float b=5.7;
pf
float *pf=&b;
9. Операции над указателями (3)
3. Доступ к данным по указателю (операция разыменования). Полученноезначение имеет тип, совпадающий с базовым типом данных указателя.
Нетипизированные указатели разыменовывать нельзя.
Примеры:
b
short c, a=5,*ptri=&a; float d,p=2.4563;
void *b=&a;
1)
ptri
1 25
5
a
c=*ptri;
2) *ptri=125;
c
5
При необходимости разыменовать нетипизированный указатель требуется
выполнить явное преобразование типа.
b
3)
*b=6; *(int*)b=6;
4) b=&p;
d=*b
b
a
d=*(float *)b
5
6
p
Явное переопределение типа указателя в С
или d=*static_cast<float*>(b);
Явное переопределение типа указателя в С++
9
10. Операции над указателями (4)
4. Операции отношения:проверка равенства (==) и неравенства (!=).
Примеры:
int sign = (p1 == p2);
if (p1!=NULL) {….} или if (p1!=nullptr)
5.Арифметические операции.
В С++ над указателями разрешены операции:
- сложение и вычитание (аддитивные операции)
-
инкремент или автоувеличение (++)
-
декремент или автоуменьшение (--)
В силу особенностей выполнения арифметических операций над
указателями, совокупность этих операций получила название
Адресной арифметики
11. 5.2.3 Адресная арифметика
<Указатель> + n <Адрес> + n*sizeof(<Тип данных>)Пример:
short a, *ptrs =&a;
1) ptrs++;
Значение
указателя
меняется
2)
Значение
указателя
меняется
ptrs+=4;
3) *(ptrs+2)=2;
Значение
указателя
не меняется!!!
12. Адресная арифметика (2)
Особенности результатов выполнения операций адреснойарифметики связано с реализацией языков С\С++.
В С\С++ принят обратный порядок размещения объектов в
памяти.
Это объясняется особенностями работы компилятора. При
разборе текста, компилятор распознает и размещает в
стек имена всех объектов, которые необходимо
разместить в памяти.
На этапе распределения памяти имена объектов выбираются
из стека и им отводятся смежные участки памяти. А так
как порядок записи в стек обратен порядку чтения,
размещение объектов оказывается обратным.
Например:
*&i2=
++*p=
int i1=10,i2=20,i3=30;
*&++i2=
*--p=
*p=
++*--p=
i1
i2
10
20
i3
30
*p++
*(p-1)=
направление увеличения адресов
13. Адресная арифметика (3)
// Ex5_1.cpp// value
i2
cout << " *p= " << *p<<endl;
#include <iostream>
// value
i2 for Ukaz p
int main()
//(p up for 1)
{int i1=10,i2=20,i3=30;
cout <<"\n *p++ ="<< *p++;
int *p=&i2;
// Value
i1
// Value and Address
cout <<"\n *p ="<< *p;
// i1,i2,i3
// value i1 up for 1
cout << "\n++*p =" << ++*p;
cout <<"\n &i1="<<&i1;
//Value i2 begin with down p
cout<<“ *&i1= " << *&i1;
cout << "\n*--p = " << *--p;
cout <<"\n &i2="<<&i2;
// Value i3 , begin down p,
cout << " *&i2= " << *&i2;
// i3 up
cout <<"\n &i3="<<&i3;
cout <<"\n++*--p ="<< ++*--p;
cout<<endl;
cout << " *&i3= " << *&i3;
return 0;
// value i2 added 1 (+ 1)
cout <<"\n *&i2="<<*&i2<<endl; }
cout<<"*&++i2"<<*&++i2<<endl;
14. Соотношение ссылки и указателя
int a,*ptri=&a, - указатель
Под ptr выделяется память 4 байта
&b=a;
- ссылка
ptri
a=b
Под b память не выделяется
…
a=3; *ptri=3; b=3;
4
Ссылка тоже физически представляет собой адрес, но в отличие от
указателя при работе со ссылками не используется операция
разыменования, т.е. ссылка – это второе имя (alias или псевдоним).
Поэтому:
Основное отличие указателя – для обращения к содержимому по
указателю нужна операция разыменования,
обращение к содержимому по ссылке осуществляется по имени
ссылки!!!
15. 5.3 Управление динамической памятью (С-style)
1. Размещение одного значенияВыделение памяти
void * malloc(size_t size); - возвращает адрес начала области
памяти; при присвоении указателю – явное преобразование типа.
Освобождение памяти
void free(void *block);
Пример:
int *a;
if ((a = (int *) malloc(sizeof(int))) == NULL)
{ printf("Не хватает памяти для числа.");
exit(1); }
*a=-244; *a+=10;
free(a);
16. Управление динамической памятью (С)
2. Размещение нескольких значенийВыделение памяти
void * сalloc(size_t n, size_t size);
Освобождение памяти
void free(void *block);
Пример:
int *list;
list = (int *) calloc(3,sizeof(int));
*list=-244;
list
*(list+1)=15;
*(list+2)=-45;
…
free(list);
4
4
4
17. Управление динамической памятью (С++-style)
1. Одно значениеОперация выделения памяти
<Указатель> =new<Имя типа>[(<Значение>)];
Операция освобождения памяти
delete <Указатель>;
Примеры:
а) int *k;
k = new int;
*k = 85;
б) int *a;
if ((a = new int(-244)) == nullptr)
{printf("Не хватает памяти для числа.");
exit(1);
delete a;
}
18. Управление динамической памятью (С++)
2. Несколько значенийОперация выделения памяти для n значений:
<Указатель> =new<Имя типа>[<Количество>];
Операция освобождения памяти:
delete [ ] <Типизированный указатель>;
Пример:
list
list+1
short *list;
2 2
list = new short [3];
*list=-244; *(list+1)=15; *(list+2)=-45;
delete[ ] list;
list+2
2
19. 5.4 Многомерные массивы и указатели.
Объявление массива:int *a;
|
int a[10];
a=calloc(10,4);
|
a[2]=15;
*(a+2)=15;
Примеры:
int list[10];
По правилам С/С++ имя массива является его адресом.
Поэтому для адресации элементов массива независимо от
способа описания можно использовать адресную
арифметику:
(list+i) &(list[i])
*(list+i) list[i]
20. Многомерные массивы и указатели (2)
int m[2][3][2];m *m => m[0][][]
**m => m[0][0][]
***m => m[0][0][0]
m[0]
m[0][2][]
m[1]=>*(m+1)
m[0][2][0] => *(*(*(m+0)+2)+0) =>*(*(*m+2))
m[i][j][k] => *(*(*(m+i)+j)+k) =>*(*(*(i+m)+j)+k)
21. Многоуровневые ссылки (Ex5_1a)
int m[]={1,2,3,4};int *mp[]={m+3,m+2,m+1,m};
mp
mp[0],*mp
mp[1],*(mp+1)
mp[2],*(mp+2)
mp[3],*(mp+3)
m
1
2
3
4
m[1],*(m+1)
или
mp[0][-2],
*(mp[0]-2),
*(*mp-2),
mp[1][-1],
*(mp[1]-1),
*(*(mp+1)-1)
22. Особенности описания и работы с памятью при обработке массивов(С++-style)
Автоматический массив (CLang):n должно быть введено до определения
массива
int a[n][n], s[n];
Динамический массив
Выделение памяти:
int **a = new int*[n],
*s = new int[n];
for (int i;i<n;i++)
a[i] = new int[n];
Память освободится автоматически!
Статический массив:
n должно быть задано
константой (например const n=10
или инструкцией препроцессора #define)
до определения массива
int a[n][n], s[n];
Память освободится автоматически!
Освобождение памяти:
for (int i;i<n;i++)
delete [] a[i];
delete [] a;
delete [] s;
23. Пример использование указателей при обработки массивов
Пример. Написать программу переформирования матрицы путем сортировкикаждой ее строки по возрастанию ее элементов.
Создание динамической матрицы
Указатель на
mas
// Ex5_2.cpp
указатель
#include <stdio.h>
#include <stdlib.h>
int **mas,*ptr;
0
int a,b,n,m,i,j,k;
1
int main()
2
{ printf("\n input n= ");
3
scanf("%d",&n);
printf("\n input m= ");
Одномерные массивы
scanf("%d",&m);
целого типа
mas=new int * [n];
for(i=0;i<n;i++)
Массив указателей
mas[i]=new int [m];
Выделение памяти под строки
матрицы
Выделение памяти под
массив указателей
23
24. Пример использования указателей при обработки массивов (2)
Заполнение матрицы даннымиfor(i=0;i<n;i++)
{ printf(" input %d elem. %d string\n",m,i);
ptr=mas[i];
Запоминаем адрес начала
строки
for (j=0;j<m;j++)
scanf("%d",mas[i]++);
Идем по элементам изменяя
mas[i]=ptr;
адрес элемента
(адресная арифметика)
}
Печать сформированной матрицы
puts("Inputed matrix");
for(i=0;i<n;i++)
{ ptr=mas[i];
for (j=0;j<m;j++)
printf("%3d",*ptr++);
printf("\n");
}
Восстанавливаем адрес
начала строки
Используем вспомогательный
указатель для перемещения по
строке
25. Пример использования указателей при обработки массивов(3)
Сортировка строк матрицыИспользование вспомогательного указателя
for(i=0;i<n;i++)
для перестановок
for(k=0;k<m-1;ptr=mas[i],k++)
for(j=0;j<m-1;ptr++,j++)
if(*ptr>*(ptr+1))
Сортировка
{b=*ptr;*ptr=*(ptr+1);*(ptr+1)=b;}
строки
Печать переформированной матрицы
методом
пузырька
puts("Sorted matrix");
for(i=0;i<n;i++)
{ ptr= *(mas+i);
for (j=0;j<m;j++)
Использование вспомогательного
printf("%3d",*ptr++);
указателя для обхода матрицы
printf("\n");
}
for(i=0;i<n;i++)
delete [] *(mas+i);
Удаление динамической матрицы.
delete [] mas;
Идет в порядке, обратном
созданию
return 0;
}
26. Варианты программы подсчета сумм строк (2)
Автоматический массив (CLang):int aa[n][n], s[n];
s
Динамический массив
Выделение памяти:
int **a = new int*[n],
*s = new int[n];
for (int i;i<n;i++)
a[i] = new int[n];
a
s
! Память будет выделена в момент
выполнения программы и будет
освобождена автоматически при
выходе из функции, в которой
объявлены массивы!
Освобождение памяти:
for (int i;i<n;i++)
delete [] a[i];
delete [] a;
delete [] s;
26
27. Пример программы подсчета сумм строк
#include <iostream>Ex03_06
#include <iomanip>
using namespace std;
int main() {
int n;
cout<<"Enter n:";
cin >> n;
int a[n][n], s[n]; // для компилятора CLang
for (int i = 0; i < n; i++) {
cout << "Enter numbers of " << i <<" string:\n";
for (int j = 0; j < n; j++) cin >> a[i][j];
}
for (int i = 0; i < n; i++){
s[i] = 0;
for (int j = 0; j < n; j++) s[i] += a[i][j];
}
for (int i = 0; i < n; i++) {
for (int j=0;j<n;j++) cout << setw(3)<< a[i][j];
cout << " sum = " << s[i] << endl;
}
return 0;
27
}
programming