Яп
.pdfУказатели на указатели
intvar = 123; // инициализация переменной var числом 123 int*ptrvar= &var; // указатель на переменную var int**ptr_ptrvar = &ptrvar; // указатель на указатель на var int***ptr_ptr_ptrvar = &ptr_ptrvar; // ук. на ук. на указатель
cout <<var; //123 cout <<*ptrvar; //123
cout <<**ptr_ptrvar; // два раза разименовываем указатель //123 cout <<***ptr_ptr_ptrvar;; // указатель третьего порядка //123
cout <<"\n ***ptr_ptr_ptrvar -> **ptr_ptrvar -> *ptrvar -> var -> "<< var << endl;
***ptr_ptr_ptrvar -> **ptr_ptrvar -> *ptrvar -> var -> 123
cout <<"\t " << &ptr_ptr_ptrvar<< " -> " << " " << &ptr_ptrvar <<" ->" << &ptrvar <<" -> " << &var <<" -> " << var << endl;
0x22ff00 -> 0x22ff04 ->0x22ff08 -> 0x22ff0c -> 123
201
Указатель-константа и указатель на константный
указуемый объект
int i1, i2;
int * const p1 = &i1; // указатель-константа. Переназначить на другую область памяти нельзя, но значение переменной менять можно
const int* p2 = &i1; // указатель на const. Нельзя модифицировать значение переменной по этому адресу.
const int* const p3 = &i1; // указатель-константа на константу. Нельзя переназначить адрес, нельзя изменить значение по этому указателю.
p1 = &i2; |
// ошибка, указатель-константа |
|
*p1 = 5; |
// правильно, ук. объект не является const |
|
p2 = &i2; |
// правильно, указатель не является const |
|
*p2 = 5; |
// ошибка, указуемый объект – константа |
|
p3 = &i2; |
// ошибка, указатель-константа |
|
*p3 = 5; |
// ошибка, указуемый объект - константа |
203 |
2/27/2023
Динамические многомерные массивы
const unsigned int DIM1 = 3; const unsigned int DIM2 = 5;
int main() { |
|
|
|
|
int **ary; |
// Указатель для доступа к двумерному массиву |
|||
// создание |
|
|
|
|
ary = new int * |
[DIM1]; // выделяет память для массива указателей на int |
|||
for (int |
i |
= 0; |
i |
< DIM1; i++) { // В цикле каждый элемент массива указателей инициализируется |
оператором new, который выделяет память для массива типа int. |
||||
ary[i] |
= |
new int [DIM2]; } // инициализация указателей |
||
// работа с массивом |
||||
for (int |
i |
= 0; |
i |
< DIM1; i++) { |
for (int j = 0; |
j < DIM2; j++) { |
|||
ary[i][j] = |
(i + 1) * 10 + (j + 1); } } |
|||
for (int |
i |
= 0; |
i |
< DIM1; i++) { |
for (int |
j = 0; |
j < DIM2; j++) { |
||
cout |
<< setw(4) << ary[i][j]; } |
|||
cout << endl; |
} |
|
||
// уничтожение |
|
|
||
for (int |
i |
= 0; |
i |
< DIM1; i++) { |
delete |
[] ary[i]; } |
|||
delete [] ary; |
|
|
||
return 0; |
} |
|
|
202 |
Типизированные указатели
•Типизированныеуказатели неявно могут быть преобразованыв указатели на void, но не обратно!!!
void*void_ptr; |
// нетепизированныйуказатель |
int*int_ptr; |
// типизированный ук. на тип int |
char*char_ptr; |
// типизированный указательна char |
void_ptr= int_ptr; |
// правильно |
char_ptr= void_ptr; |
// правильно в С, но ошибка С++ |
char_ptr= int_ptr; |
// предупреждение в С, но ошибка в С++ |
204
51
Указатели на функции
•Указатели могут ссылаться на функции. Имя функции, как и имя массива само по себе является указателем, то есть содержитадрес входа.
/*тип данных*/(*/*имя указателя*/)(/*список аргументовфункции*/);
•Тип данных определяем такой, который будет возвращать функция, на которую будет ссылаться указатель. Символ указателя и его имя берутся в круглые скобочки, чтобы показать, что это указатель, а не функция, возвращающая указатель на определённый тип данных. После имени указателя идут круглые скобки, в этих скобках перечисляются все аргументы через запятую как в объявлении прототипа функции. Аргументы наследуются от той функции, на которую будет ссылаться указатель.
205
Зачем нужны указатели???
3.Доступ к большимструктурам данных.Используютдля косвенного обращения к большимструктурам данных, повышая скорость и сокращая затраты на организацию доступа.
int *ptr; |
|
int a[100]; |
|
ptr= &a[0]; |
// явный адрес первого элемента |
ptr=a; |
// неявный адрес первого элемента |
*(ptr+1) -эквивалентно a[i]
207
2/27/2023
Зачем нужны указатели???
1.Повышениеэффективности.Вместо копирования или пересылкив памяти большойструктуры данных можно скопировать или переслать толькоуказатель на эту структуру.
2.Динамическиеструктуры.С помощьюзаписей и указателей можнореализовать структуры данных, которые растут и сжимаются в период выполненияпрограммы. Например,такие как списки и деревья. Помимо элементов данных самой структуры, узел содержитодин или несколькоуказателей со ссылками на другие узлы.
Элемент связного списка Две секции:
водной секции (по имени info) содержится порция данных,
вдругой — адрес соседнего элемента структуры.
206
ССЫЛКИ
208
52
Ссылки
•Ссылкатип данных,являющийся скрытой формой указателя, который при использованииавтоматически разыменовывается.
•Ссылка можетбыть объявленакак другим именем,так и как псевдонимомпеременной,на которую ссылается.
/*объявление ссылки*/; /*тип*/ &/*имя ссылки*/ =/*имя переменной*/;
•Переменная,на которую ссылается ссылканазывается
ссылочнойпеременной.
•Изменениезначения содержащегося в ссылкевлечёт изменениеэтого значенияв переменной, на которую ссылаетсяссылка.
209
Отличия указателей и ссылок
•Основноеназначениеуказателя– это организация динамических объектов, то есть размер, которых может меняться (увеличиваться или уменьшаться).
•Основноеназначениессылкиорганизации прямого доступа к тому, или иному объекту.
•Главное отличие состоит во внутреннеммеханизмеработы:
•Указателиссылаютсянаучасток в памяти, используя его адрес.А ссылкиссылаютсяна объект, по его имени (тоже своего рода адрес).
211
2/27/2023
Ссылки
intvalue= 15;
int&reference= value; // инициализация ссылки значением переменной value
cout <<"value = " << value << endl; // value==15
cout <<"reference = " << reference << endl; // reference= =15
reference+=15; // изменяем значение переменной value посредством изменения значения в ссылке
cout <<"value= " << value << endl; // значение поменялось как в ссылке value==30 ;
cout <<"reference = " << reference << endl; // так и в ссылочной переменной reference= =30
210
ДИНАМИЧЕСКАЯ ПАМЯТЬ
212
53
Распределение памяти
Программа обычнопомещается в отдельную, непрерывную область.
Код – машинные команды,являются результатом компиляции. Константы– небольшиеконстанты, содержащиеся внутри команды.
Стек – стековая память, используемаядля записей активации, которые содержатпараметры, переменныеи ссылки. Также для временныхпеременныхпри вычислениях.
Статические данные– переменные,объявленные в главной программеи других местах.
Куча – область данных, из которой данные динамически выделяют специфическими командами для зыка.
213
Дефрагментация кучи
1.Частичная дефрагментация. Если активные блоки кучи нельзя перемещать(или перемещениеобходится слишком дорого), то можно объединятьтолькосмежные блокииз списка свободного пространства.
2.Полная дефрагментация. Если активные блокиможно перемещать,тогда все активные блоки перемещаются в один конец кучи, а все свободноепространство оказывается сосредоточеннымна другом конце, в одном непрерывномблоке.
•Полная дефрагментация подразумевает,что при перемещенииактивного блока все указатели на него модифицируются нужным образом и способныуказать на его новое местоположение.
•С помощьювиртуальнойпамятименеджеркучи может продолжатьвыделениединамическойпамяти почти
215
бесконечно,не сталкиваясь с проблемойфрагментации.
2/27/2023
Распределение памяти
•Менеджер кучи - это компонентисполняющей системы, который выделяети освобождаетпамять.
•Это делается посредствомподдержкиспискасвободных блоков.
•Когда сделан запрос на выделениепамяти, она ищется в этом списке, а при освобождении блок снова подсоединяетсяк списку свободных блоков.
•Проблема фрагментации- в ходевычислений блок постепенноразбивается на меньшиеблоки посредством процедур выделения памяти, ее восстановления и повторногоиспользования.При этом блокисвободного пространства расщепляются на все более мелкиеобласти.
•Дефрагментациякучи - объединениесвободныхблоков в блокибольшегоразмера.
214
Работа с кучей
Си:
void malloc(size_tколичество_байтов); // выделить память
количество_байтовэто размер памяти,необходимой для размещения динамического объекта. Тип size_t задан в <stdlib.h> как некоторый целыйбез знака.
void free(void*p); // освободить память
p = malloc(128); if(!p) {
printf("Памяти не хватает!\n"); exit(1);
}
free(p);
Вызов функции free( ) с ошибочнымаргументом разрушит всю систему динамическогораспределения памяти. 216
54
Работа с кучей
•В языке С++ для работы с динамическимиобъектами существуют операторы new и delete[].
Пример: int ptr1;
int ptr2 = newint[256];// Размещение динамического массива
ptr1 = ptr2; |
|
delete [] ptr2; |
// Удаление динамического массива |
217
Повисшие указатели и утечки
•Повисший указательэто указатель на память, которая используетсядля других целей. Типичный случай - память уже перераспределена.
•К повисшемууказателю приводитоператорdispose() в
Pascal, delete в С, С++.
•Повисшие указателипонижаютбезопасностьпрограмм.
•Память, которая распределена,но недоступна, называется
мусором(garbage).
•Говорят,что программы,которые создают мусор, дают утечки памяти (memoryleaks).
•К утечкам памяти могут привести присваивания между указателями.
•Утечкапамяти может понизить производительность,но не безопасность.
219
2/27/2023
УТЕЧКИ ПАМЯТИ И ПОВИСШИЕ УКАЗАТЕЛИ
218
Повисшие указатели и утечки
•p и q - указатели на какие-то элементы.
•p:= q приводит к тому, что p и q указывают наодин и тот же элемент.
•элемент, на который ранееуказывал p, по-прежнему остается в памяти, нотеперь он недоступен. Это утечка памяти.
•Выполним теперь оператор delete(p).Теперь оба указателя становятся «повисшими», поскольку хранят адрес уничтоженного объекта.
•Предположим, эта ячейка памяти задействуется под новую, секретную информацию. Тогда через указателиможно похитить все секреты!
Пример на Си++
int * ptr1 = new int; // выделить первый блок int * ptr2 = new int; // выделить второй блок
ptr2 = ptr1; // второй блок теперь недоступен!!! Возникла повисшая ссылка
220
55
Повисшие указателии утечки. Способы устранения
1.Ручной. присваивать указателю (параметру оператора dispose или delete) значение nil или null.
•Реализовать это можно как отдельный оператор или как завершающую фазу выполнения оператора освобождения динамической памяти.
2.Автоматический. Использование сборщика мусора, встроенного в некоторые языки программирования.
•Задача сборщика мусора – повторно использовать мусор, идентифицируя недоступные блоки памяти и возвращая их менеджеру динамической памяти:
oдля каждого блока ведется счетчик текущего числа указателей, ссылающихся на этот блок, и автоматически освобождает блок, когда счетчик доходит до нуля.
oотмечает все доступные блоки и затем собирает немаркированные (а следовательно недоступные) блоки.
221
ПОДПРОГРАММЫ
•Подпрограмма– группа операторов, которая можетбыть выполненанеоднократно.
Т.о. её можнорассматривать как:
1.Сегмент программы,который должен выполнятьсяна разных стадиях вычисления, можетбыть написан один раз в виде подпрограммы,а затем многократновыполняться.
2.Логическая единица декомпозиции программы
(компиляции).
223
2/27/2023
ПОДПРОГРАММЫ. ВЫЗОВ ПОДПРОГРАММ
222
ПОДПРОГРАММЫ
1.Функции - расширяют встроенные в язык операции, возвращают результат, который можноиспользоватьв выражениях, как правило вызываются из выражений.
2.Процедурыони расширяют встроенныев язык операторы, просто выполняютдействие, результат не возвращают, как правило вызываются отдельнымоператором.
•В языке С используютсятолько функции.
•Если функция не должна ничего возвращать то её тип обозначается как void.
•В ранних языках программирования подпрограммымогли только выполнятьдействия (былипроцедурами),не имели имени и параметров.
•Сегодня подпрограммыпревратились в мощное средство ООП, называемое методамиобъектов.
224
56
ПОДПРОГРАММЫ. ОСНОВНЫЕ ПОНЯТИЯ
•Описание(интерфейс)подпрограммы– указание её имени,
параметрови типа возвращаемого значения (если есть).
•Имя подпрограммы– любойдопустимый идентификатор языка программирования,покоторому транслятор однозначновосстанавливает соответствие между вызывающим и вызываемым кодом.
•Тело подпрограммы– группа операторов, из которых состоит подпрограмма.
•Вызов подпрограммы– оператор, указывающий на необходимостьвыполненияподпрограммы.В большинстве языков программирования это указание имени(иногда предваряемоеключевым словомCALL) и фактических параметров.
225
ПОДПРОГРАММЫ. ПАРАМЕТРЫ
intsquare (intx) |
// x – формальныйпараметр |
{ |
|
return x x; |
// в теле – формальные параметры |
}; |
|
square(2);// Вызов функции, «2»- фактический параметр
• Различают три разновидностифактического параметра:
1.локальныйобъект данных,принадлежащийвызывающей программе;
2.нелокальныйобъектданных,видимыйиз вызывающей программы;
3.результат вычисления функции,вызванной из вызывающей программы,который возвращается в точку
вызова. 227
2/27/2023
ПОДПРОГРАММЫ. ПАРАМЕТРЫ
•Данные передаются подпрограммев виде последовательностизначений, называемых параметрами.
•Формальныйпараметр– некоторое значение (в т.ч. переменнаяили адрес памяти), передаваемоевнутрь подпрограммыи использующееся ею для вычислений(в т.ч. и для возвращения значений).
•Это объявление, которое находитсяв объявлении подпрограммы.Вычисление в телеподпрограммыпишется в терминах формальныхпараметров.
•Фактическиепараметры– конкретныезначения,
подставляемые на места формальныхпараметров подпрограммы.
•Это значение, которое вызывающая программапередает подпрограмме.
226
ПОДПРОГРАММЫ. ПАРАМЕТРЫ
228
57
Преимущества подпрограмм
1.Уменьшениесложностипрограммирования.
Использованиеимени подпрограммыпозволяет абстрагироваться от деталейреализации, думать в терминах операций, относящихся к решаемой проблеме.
2.Закрытостьреализациимодификация алгоритмавнутри подпрограммыне воздействует на остальную программу.
3.Модульность программразбиение программына небольшиекуски, которые можно рассматривать отдельно => подпрограммыпозволяют управлять болеекрупными программами.
4.Расширениевозможностейязыковпрограммирования-
создание библиотек. Например, математическиефункции типа sin( ), log( ) обеспечиваются библиотекой, как и процедуры ввода-вывода.
229
Методы передачи параметров
•Сопоставлениемежду фактическими и формальными параметрами в вызове подпрограммыназывают передачей параметров.
•Методы передачи параметровэто способы, которыми параметрыпередаются в/из подпрограммы.
•Два способа, которые применяютсядля установления такого связывания:
1.Позиционное сопоставление.
2.Сопоставление по имени.
231
2/27/2023
Подпрограммы в разных языках
•На языке С спецификация функциизаписывается в виде: float Fun(float A, intB)
•Процедура на языке С оформляется как функция, которая не возвращает результат, ее заголовок долженначинаться с ключевого слова void:
void Proc(float A, intB, float C, int D)
•Спецификация в языке Ada более наглядновыражает возможные различия в смысле параметров:
procedure Proc (A: in float; B: in integer; C: in out float; D: out float)
•in - передача из фактического в формальныйпараметр, out - из формальногов фактический параметр, in out - двунаправленнаяпередача.
230
Позиционное сопоставление
•Соответствие устанавливается на основеих позиций в списках фактических и формальныхпараметровдва параметра,которыезанимаютодинаковыепозиции в списках,образуютпару.
•Обычноколичество формальныхи фактических параметров должно совпадать,чтобы соответствие между ними было
взаимно однозначным.
•Python, Ruby, Fortran , PHP, С++ и Ada формальныепараметры могут иметьзначения по умолчанию.
•Используетсяесли формальномупараметру не передается никакого фактического параметра.
Python:
def salary(hours, tax_free= 1, hour_rate) # tax_free задан
my_money= salary(120,hour_rate= 10.0) # можно вызвать так
232
58
Сопоставление по имени
•Можноявно указать, какой формальныйпараметрдолжен соответствовать данномуфактическому параметру.
•Недостаток - пользовательподпрограммыдолжен знать имена формальныхпараметров.
Ada: Sub(X => A, Y => 27);
Python:
adder (size = the_size, # size, list, sum - формальные
list = the_list, # the_size, the_list, the_sum - фактические sum = the_sum)
C++ - параметрыпо умолчанию указываются в конце списка float salary (inthours, float hour_rate, inttax_free= 1); my_money= salary(120,10.0);// tax_free можно не указывать
233
Передача параметров по значению
•При передачепо значению формальныйпараметрх подпрограммыР(х) получаетзначениефактического параметра.
•Любые изменения значения формального параметра, произошедшие во время выполнения подпрограммы, теряются, когда подпрограмма завершает свое выполнение.
•В языках Pascal и С она рассматриваетсякак основной метод передачи параметров.
235
2/27/2023
МЕТОДЫ ПЕРЕДАЧИ ПАРАМЕТРОВ
234
Передача параметров по ссылке
•Формальныйпараметрпревращается в синонимместа размещения фактического параметра. Т.е. подпрограмме становится доступным указатель на местоположениеэтого объекта (то есть его l-значение).
Пример C++
void swapc ( int px, int py) { intz;
z= px; px= py; py= z;
}
Вызов swapc(&a, &b) приводитк следующим действиям:
59
Передача параметров по ссылке
Пример C#
void adder(ref inttotal, intfee) { . . . } . . . # ref - по ссылке adder(ref count, additive);
•Высокая эффективностькак времени, так и памяти. Не надо копировать значения фактических параметров, а затем отдельнообновлять их.
•Доступ к обрабатываемымпараметрамзамедляется, поскольку используется косвенная адресация.
•Могут возникнуть неумышленныеи ошибочные изменения фактических параметров.
•Появляется возможность возникновенияпсевдонимов (алиасов) фактических параметров.Псевдонимы снижают читабельностьи надежность программ,а также усложняют их проверку.
237
Передача параметров по значению-результату
•Доступ к обрабатываемым в подпрограмме параметрамускоряется, поскольку в косвенной адресации нет необходимости.
•Безопасность хранения фактических параметров повышается, так как канал связи с ними открывается перед началомобработки в режиме приема и после окончания обработки в режиме записи. Во время выполнения тела подпрограммыканал закрыт.
•Необходимость хранить параметрыв нескольких местах и тратить время на копирование их значений и адресов.
•Сохраняется проблема псевдонимов.
239
2/27/2023
Передача параметров по значению-результату
•Фактические параметры,не имеющие места размещения, передаются по значению.
•Фактические параметры,имеющие место размещения, обрабатываются так:
1.Этап copy-in.Вычисляются значения и места размещения фактических параметров. Значения присваивают формальнымпараметрам.
2.Выполнениетела подпрограммы.Здесь обрабатываются формальныепараметры.
3.Этап copy-out.После выполнениятела подпрограммы конечные значения формальныхпараметров копируются обратнов места размещения, вычисленныена этапе copy-in.
238
Передача по результату
•Параметр,передаваемый по результату,используетсятолько для передачи результатоввычисленияназад в вызывающую программу.
•ПримерС# - спецификатор outпри форм.параметре
void Report(outinta, out intb) {a= 29; b = 57;} obj.Report(outm, outm);<- одинаковыеимена
•если первымприсваиваетсязначениепараметраa, то значениефактическогопараметравызывающегомодуля - 57; если первымприсваиваетсязначениеb, тогда значениебудет равно29.
•Т.к.порядок можетзадаватьсяреализацией,различные реализацииязыкабудут генерировать разные результаты.
•Моментдля вычисленияадреса фактическогопараметра может определятьсяв начальной фазе вызоваподпрограммыили при
возвращениииз нее - неопределенность должен снять |
|
разработчик реализацииязыка. |
240 |
60