Утечки памяти и повисшие указатели
• Повисший указатель – указатель на память, которая используется для других целей. Типичный случай - память уже перераспределена. (пример – память уже перераспределена)
• К повисшему указателю приводит оператор dispose() в паскале, delete в с, с++.
• Повисшие указатели понижают безопасность программ.
• Мусор – память, которая распределена, но недоступна.
• Мусорные ссылки – ссылка на область памяти которая не используется или недоступна. (память которая распределена но недоступна)
• Говорят, что программы, которые создают мусор, дают утечки памяти.
• К утечкам памяти могут привести присваивания между указателями.
•Утечка памяти может понизить производительность, но не безопасность.
P и q – указатели на какие-то элементы.
P:=q приводит к тому, что p и q указывают на один и тот же элемент.
Элемент, на который ранее указывал р по-прежнему остается в памяти, но теперь он недоступен. Это утечка памяти.
Delete(p). Теперь оба указателя становятся повисшими, поскольку хранят адрес уничтоженного объекта.
◘ Способы устранения:
1. Ручной. Присваивать указателю(параметру оператора dispose или delete) значение nil или null.
2. Реализовать можно как отдельный оператор или как завершающую фазу выполнения оператора освобождения динамической памяти.
3. Автоматический. Использование сборщика мусора, встроенного в некоторые языки программирования.
• Задача сборщика мусора – повторно использовать мусор, идентифицируя недоступные блоки памяти и возвращая их менеджеру динамической памяти:
•Для каждого блока ведется счетчик текущего числа указателей, ссылающихся на этот блок и автоматически освобождая блок.
•Отмечает все доступные блоки и затем собирает немаркированные блоки.
53. Концепция виртуальной памяти. Страничная организация памяти.
Виртуа́льная па́мять (англ. virtual memory) — метод управления памятью компьютера, позволяющий выполнять программы, требующие больше оперативной памяти, чем имеется в компьютере, путём автоматического перемещения частей программы между основной памятью и вторичным хранилищем (например, жёстким диском).
В системе с виртуальной памятью используемые программами адреса, называемые виртуальными адресами, транслируются в физические адреса в памяти компьютера. Трансляцию виртуальных адресов в физические выполняет аппаратное обеспечение, называемое блоком управления памятью. Для программы основная память выглядит как доступное и непрерывное адресное пространство, либо как набор непрерывных сегментов, вне зависимости от наличия у компьютера соответствующего объёма оперативной памяти. Управление виртуальными адресными пространствами, соотнесение физической и виртуальной памяти, а также перемещение фрагментов памяти между основным и вторичным хранилищами выполняет операционная система.
◘Применение виртуальной памяти позволяет:
• освободить программиста от необходимости вручную управлять загрузкой частей программы в память и согласовывать использование памяти с другими программами
• предоставлять программам больше памяти, чем физически установлено в системе
• в многозадачных системах изолировать выполняющиеся программы друг от друга, путём назначения им непересекающихся адресных пространств (см. защита памяти)
Страничная память — способ организации виртуальной памяти, при котором единицей отображения виртуальных адресов на физические является регион постоянного размера (т. н. страница). Типичный размер страницы — 4096 байт, для некоторых архитектур — до 128 КБ[1].
Основное применение страничного преобразования адреса - организация виртуальной памяти. Виртуальная память позволяет использовать программам, больший объем памяти, чем установленный на компьютере физический объем памяти. Остальная информация может быть сброшена на внешний носитель.
Управление страничным разбиением памяти обычно возлагается на специальную микросхему MMU (Memory Managment Unit - устройство управления памятью). В микропроцессоре i80486 и выше это устройство встроено в процессор.
Как и сегментация, страничная организация памяти связана с преобразованием виртуального адреса (в данном случае линейного) в физический. В страничном преобразовании базовым объектом памяти является блок фиксированного размера, называемый страницей (page). Размер страницы - 4 Кбайт.
Организация памяти в виде страниц борется с двумя проблемами:
Внешней фрагментацией – используются блоки фиксированного размера в виртуальной и физической памяти, т.е. все запросы на выделение памяти будут кратны, не будет оставаться некратных зон.
Внутренней фрагментацией – блоки достаточно малого размера, поэтому (К) будет мал.
С точки зрения программиста:
Процессам виртуальное адресное пространство предоставляется непрерывным, от байта 0 до байта N;
N зависит от аппаратной поддержки (например 32бита — адрресное пространство 4Гб), делится соответственно.
В реальности виртуальные страницы распределены по страницам физической памяти далеко не непрерывно и не один к одному. Это два разных мира – физические страницы и виртуальные страницы. Это ключевой аспект, который надо понимать.
С точки зрения менеджера памяти:
Эффективное использование памяти из-за очень низкой внутренней фрагментации.
Внешняя фрагментация полностью отсутствует и не нужно дефрагментировать.
С точки зрения защиты:
Процесс имеет доступ только к своему адресному пространству.
Допущение – все страницы виртуальной памяти всегда находятся в страницах физической памяти.
Не будем думать, что есть только виртуальные страницы, а физических – их нет, т.е. полное отображение виртуальной и физической памяти.
54. Сегментный принцип организации памяти. Сегментация памяти.
Сегментная организация памяти (segmentation) - схема распределения памяти в виде сегментов переменной длины, соответствующая пользовательской трактовке распределения памяти, т.е. логической структуре программ и данных. С точки зрения пользователя (разработчика программы), программа – это набор модулей кода и данных, каждому из которых должен соответствовать свой сегмент в памяти. Сегмент – логическая единица распределения памяти, предназначенная для размещения в памяти одного модуля программного кода или данных. Например, в виде сегментов памяти могут быть представлены:
основная программа;
процедура;
функция;
метод;
объект;
набор локальных переменных;
набор глобальных переменных;
общий блок данных (например, COMMON-блок в языке FORTRAN);
стек;
таблица символов;
массив.
Архитектура сегментной организации памяти
Многие принципы архитектуры сегментной организации схожи с принципами страничной организации, однако во всех случаях приходится учитывать, что длина сегмента переменна, и хранить ее в явном виде в таблицах.
Таблица сегментов – служит для отображения логических адресов в физические при сегментной организации памяти. Каждый ее элемент содержит следующую информацию:
base – начальный адрес сегмента в оперативной (физической) памяти;
limit – длину сегмента.
Базовый регистр таблицы сегментов - segment-table base register (STBR) содержит адрес таблицы сегментов в памяти.
Регистр длины таблицы сегментов - segment-table length register (STLR) содержит число сегментов, используемое программой.
Номер сегмента s корректен, если s < STLR.
Перемещение (relocation) программ и данных при сегментной организации динамическое, т.е. выполняется во время исполнения программы с помощью таблицы сегментов.
Возможен общий доступ (sharing) нескольких процессов к одному и тому же сегменту, т.е. поддерживается концепция разделяемых сегментов. При этом логический номер общего сегмента для разных процессов будет одним и тем же.
Стратегии распределения памяти при сегментной организации: метод первого подходящего или метод наиболее подходящего Метод наименее подходящего при сегментной организации смысла не имеет, так как он не улучшает ситуацию с фрагментацией (ввиду переменной длины сегментов). Соответственно, при сегментной организации возможна внешняя фрагментация, для борьбы с которой применяется компактировка.
Защита при сегментной организации организована аналогично защите при страничной организации, однако, ввиду того, что каждый сегмент выполняет определенную логическую функцию в программе, дополнительно с каждым сегментом связываются его признаки защиты.Таким образом, в каждом элементе таблицы сегментов хранятся:
validation-бит (аналогично страничной организации): значение бита, равное 0, означает, что сегмент неверный, т.е. не принадлежит логической памяти процесса;
полномочия чтения (read) / записи (write) / исполнения (execute) – каждое кодируется одним битом; значение бита, равное 0, означает, что процесс не имеет данных полномочий.
Например, если сегмент является сегментом данных, то система устанавливает в таблице сегментов бит защиты от исполнения равным 0. Если это сегмент кода, то целесообразна установка в 0 битов защиты от чтения и от записи.
Биты защиты связываются с сегментами. Совместный доступ к коду осуществляется на уровне сегментов.
Преимущества сегментации:
· Сегменты не мешают друг другу.
· Начальный адрес процедуры всегда начинается с (n,0). Что упрощает программирование.
· Облегчает совместное использование процедур и данных.
· Раздельная защита каждого сегмента (чтение, запись).
55. Указатели. Операции наад указателями. Типизированные и не типизированные указатели.
Указатели
Переменная, значением которой является адрес другой переменной. Объект на который указывают называется указуемым, или обозначаемым объектом. Для описания указателей используется операция косвенной адресации. & указывает, что нам нужен адрес, а не значение переменной. Указатели применяются скорее для вычисления с адресами ячеек, чем с их содержимым. Разница между переменной указателем и указуемым объектом.
Различия между указателем константой и указателем на константный указуемый объект.
Типизированные указатели не явно могут быть преобразованы в указатели на void, но не обратно.
Указатели нужны для реализации динамических структур данных, например списков, деревьев и т.д.
<Указатели>
• Это переменная, значениями которой является адрес другой переменной.
• Значение указательного типа – адрес.
• Указательная переменная(указатель) содержит адрес другой переменной или константы.
• Объект, на которой указывают, называется указуемым или обозначаемым объектом.
• Указатель ссылается на блок данных из области памяти, причем на его начало.
• Указатель может ссылаться на переменную или функцию.
• Размер (формат) адреса зависит только от величины адресного пространства компьютера и специфики его организации.
Обозначаются со *.
• Указатель - величина, предоставляющая косвенный доступ к элементам известного типа. (Типизированное средство косвенного доступа к объектам данных)
Int var =123;
Int *ptrvar;//объявление указателя
Ptrvar=&var;присвоили адрес переменной указателю
Cout<<&var;адрес переменной var
Cout<<ptrvar;адрес переменной var
Cout<<var;значение переменной 123
Cout<<*ptrvar;вывод значения содержащегося в переменной через указатель, операцией разименования указателя 123.
• Операции над указателями:
1. Объявление *р
2. Получение адреса р=&а
3. Разыменование x=*p –получаем объект, на который указатель ссылается.
4. Получение адреса указателя &p
5. Увеличение указателя на единицу ++p
6. Вычитание p2-p1
• Если р2 и р1 указывают на элементы типа long, то р2-р1=2, означает, что указываемые переменные разделены двумя значениями типа long, а не двумя байтами.
Int a;целое число
Int *a; указатель на целое
Int **a;указатель на указатель
Int a[10];массив
Int *a[10]; массив из десяти указателей на целые
Int (*a)[10];указатель на массив из десяти целых
Int (*a)int;указатель на функцию
Int(*a[10])int; массив из 10 указателей на функции, которые берут целый аргумент и возвращают целое
Разница между переменной-указателем и указуемым объектом
Int i1=10;
Int i2=20;
Int*ptr1=&i1;птр указывает на и1
If(ptr1>ptr2)//истина(значение адресов сравниваются)
If(*ptr1>*ptr2)//сравниваем переменные
*ptr1==*ptr2//косвенно приравниваем и1=и2=20
If(ptr1==ptr2)//ложь, указатели до сих пор разные
If(*ptr1==*ptr2)//истина, объекты равны
Ptr1=ptr2//указывают на и2, ptr1=ptr2
Указатели на указатели
• Указатели могут ссылаться на другие указатели. При этом в ячейках памяти, на которые будут ссылаться первые указатели, будут содержаться не значения, а адреса вторых указателей.
• Число символов * при объявлении указателя показывает порядок указателя.
• Чтобы получить доступ к значению, на которое ссылается указатель его необходимо разыменовывать соответствующее количество раз.
Int var 123;
Int *ptrvar=&var;указатель на переменную
Int **ptr_ptrvar=&ptrvar;указатель на указатель на переменную
Cout<<var;123
Cout<<*ptrvar;123
Cout<<**ptr_ptrvar;123
Int i1,i2;
Int *const p1=&i1;указатель-константа. Переназначить на другую область памяти нельзя, но значение переменной можно.
Const int*p2=&i1;нельзя модифицировать значение переменной по этому аресу.
Const int*const p3=&i1; указатель-константа на константу. Нельзя перезначать адрес, нельзя изменить значение по этому указателю.
P1=&i2 ошибка, указатель-константа
*p1=5 правильно, т.к указатель объект не является константой
P2=&i2; правильно, указатель не const
*p2=5; ошибка, указуемый объект -константа
P3=&i2; ошибка, указатель-константа
*p3=5; ошибка, указуемый объект-константа
Типизированные указатели
• Неявно могут быть преобразованы в указатели void, но не обратно!
Void *void_ptr;нетипизированный указатель
Int *int_ptr;типизированный ук. на тип int
Char *char_ptr;типизированный указатель на char
Void_ptr=int_ptr;правильно
Char_ptr=void_ptr;правильно в С, но ошибка в С++
Char_ptr=int_ptr;предупреждение в С, но ошибка в С++
Указатели на функции
• Указатели могут ссылаться на функции. Имя функции, как и имя массива само по себе является указателем, то есть содержит адрес входа. Int(*a)(int)
Зачем нужны указатели??
1. Повышение эффективности. Вместо копирования или пересылки в памяти большой структуры данных можно скопировать или переслать только указать на эту структуру.
2. Динамические структуры. С помощью записей и указателей можно реализовать структуру данных, которые растут и сжимаются в период выполнения программы. Например, такие как списки и деревья. Помимо элементов данных самой структуры, узел содержит один или несколько указателей со ссылками на другие узлы.
Две секции: в одной секции(into) содержится порция данных, в другой – адрес соседнего элемента структуры.
3. Доступ к большим структурам данных. Используют для косвенного обращения к большим структурам данных, повышая скорость и сокращая затраты на организацию доступа.
Int *ptr;
Int a[100];
Ptr=&a[0];явный адрес первого элемента
Ptr=a;неявный адрес
*(ptr+1)- эквивалентно a[i]
[Отличия указателей и ссылок]
• Основные назначение указателя – организация динамических объектов, то есть размер, которых может меняться( увеличиваться или уменьшаться)
• Основные назначение ссылки – организации прямого доступа к тому, или иному объекту.
• Указатели ссылаются на участок памяти, используя его адрес. А ссылки на объект, по его имени(тоже своего рода адрес).
Указатели– это переменная, значением которой является адрес другой переменной. Указатель ссылается на блок данных из области памяти, причём на самое его начало.
Значение указательного (ссылочного) типа (pointer type) – это адрес. Объект, на который указывают, называется указуемым или обозначаемым объектом (designated object).
Различают типизированные и нетепизированные (void *) указатели, указатели на данные и на функции. При этом множества допустимых операций для указателей разных видов различны.
Размер (формат) адреса зависит только от величины адресного пространства компьютера и специфики его организации. Указатель позволяет предоставить косвенный доступ к элементам
известного типа (типизированное средство косвенного доступа к объектам данных).
int var = 123;
int *ptrvar; // объявление указателя
ptrvar = &var; // присвоили адрес переменной указателю
cout << &var; // адрес переменной var 0x22ff08
cout << ptrvar; // адрес переменной var 0x22ff08
cout << var; // значение в переменной var 123
cout << *ptrvar; // вывод значения содержащегося в переменной var через указатель, операцией разименования указателя 123
Основными операциями с указателями являются:
1. Объявление. int *pa; float *pb.
2. Получение адреса переменной. p = & a.
3. Разыменование. x = *p - получаем объект, на который указатель ссылается.
4. Получение адреса указателя. &p.
5. Увеличение указателя на единицу. ++p (перемещение к адресу следующего элемента массива).
6. Вычитание. p2 – p1 (результат отображается в тех же единицах, что и размер данного типа переменной).
Для типизированных указателей запрещены операция разадресации, инкремента и т.п.
Для указателя на функции определена операция вызова функции.
Приведем несколько примеров объявлений более сложных указателей.
int a; // Целое число
int *a; // Указатель на целое
int **a; // Указатель на указатель на целое
int a[10]; // Массив из десяти целых
int *a[10]; // Массив из десяти указателей на целые
int (*a)[10]; // Указатель на массив из десяти целых
int (*a)(int); // Указатель на функцию, которая берет целый аргумент и возвращает целое
int (*a[10])(int); // Массив из десяти указателей на функции, которые берут целый аргумент и возвращают целое
При работе с указателями важно четко понимать разницу между самим указателем (как переменной) и указуемым объектом. Приведем несколько примеров, иллюстрирующих эту разницу.
int i1 = 10;
int i2 = 20;
int *ptr1 = &i1; // ptr1 указывает на i1// ptr1 = 0x22ff04
int *ptr2 = &i2; // ptr2 указывает на i2// ptr2 = 0x22ff00
if (ptr1 > ptr2) {}; // истина т.к. 0x22ff04>0x22ff00
if (*ptr1 > *ptr2){}; // сравниваем сами переменные, false
*ptr1 = *ptr2; // косвенно приравниваем i1=i2=20.
if (ptr1 == ptr2) {}; // false, указатели до сих пор разные
if (*ptr1 == *ptr2) {}; // true, обознач. объекты равны
ptr1 = ptr2; // указывают на i2, ptr1=ptr2=0x22ff00
Указатели могут ссылаться на другие указатели.
При этом в ячейках памяти, на которые будут ссылаться первые указатели, будут содержаться не значения, а адреса вторых указателей.
Число символов * при объявлении указателя показывает порядок указателя.
Чтобы получить доступ к значению, на которое ссылается указатель его необходимо разыменовывать соответствующее количество раз.
int var = 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;
cout << "\t " << &ptr_ptr_ptrvar<< " -> " << " " <<&ptr_ptrvar << " ->" << &ptrvar << " -> " << &var << " ->" << var << endl;
Результатом выполнения двух последних строк будет
***ptr_ptr_ptrvar -> **ptr_ptrvar -> *ptrvar -> var -> 123 0x22ff00 -> 0x22ff04 ->0x22ff08 -> 0x22ff0c -> 123
Важно также понимать различие между указателем-константой и указателем на константный объект. В первом случае можно менять значение указуемого объекта, но нельзя менять значение самого указателя и переназначать его на другую область памяти. Во втором случае значение указуемого объекта менять нельзя, но сам указатель можно переназначать на другую область памяти.
Приведем примеры, демонстрирующие эти различия.
int i1, i2;
/*
указатель-константа. Переназначить на другую область
памяти нельзя, но значение переменной менять можно
*/
int * const p1 = &i1;
/*
указатель на const. Нельзя модифицировать значение
переменной по этому адресу.
*/
const int* p2 = &i1;
/*
указатель-константа на константу. Нельзя переназначить
адрес, нельзя изменить значение по этому указателю.
*/
const int* const p3 = &i1; // p1 = &i2; // ошибка,указатель-константа
*p1 = 5; // правильно, ук. объект не является const
p2 = &i2; // правильно, указатель не является const
*p2 = 5; // ошибка, указуемый объект – константа
p3 = &i2; // ошибка, указатель-константа
*p3 = 5; // ошибка, указуемый объект - константа
Типизированные указатели неявно могут быть преобразованы в указатели на void. Обратное преобразрвание может привести к ошибке.
void *void_ptr; // нетепизированный указатель
int *int_ptr; // типизированный ук. на тип int
char *char_ptr; // типизированный указатель на char
void_ptr = int_ptr; // правильно
char_ptr = void_ptr; // правильно в С, но ошибка С++
char_ptr = int_ptr; // предупреждение в С, ошибка в С++
Основные преимущества использования указателей:
1. Повышение эффективности. Вместо копирования или пересылки в памяти большой структуры данных можно скопировать или переслать только указатель на эту структуру.
2. Динамические структуры. С помощью записей и указателей можно реализовать структуры данных, которые растут и сжимаются в период выполнения программы. Например, такие как списки и деревья. Помимо элементов данных самой структуры, узел содержит один или несколько указателей со ссылками на другие узлы.
3. Доступ к большим структурам данных. Используют для косвенного обращения к большим структурам данных, повышая скорость и сокращая затраты на организацию доступа.
Например для доступа к элементам массива можно использовать как явное обращение, так и косвенную адресацию, как это показано в следующем примере.
int *ptr;
int a[100];
ptr = &a[0]; // явный адрес первого элемента
ptr =a; // неявный адрес первого элемента
*(ptr+1); // эквивалент операции a[i]
56. Динамические структуры данных. Реализация динамических структур данных с помощью указателей.
В языках программирования (Pascal, C, др.) существует и другой способ выделения памяти под данные, который называется динамическим. В этом случае память под величины отводится во время выполнения программы. Такие величины будем называть динамическими. Раздел оперативной памяти, распределяемый статически, называется статической памятью; динамически распределяемый раздел памяти называется динамической памятью (динамически распределяемой памятью).
Использование динамических величин предоставляет программисту ряд дополнительных возможностей. Во-первых, подключение динамической памяти позволяет увеличить объем обрабатываемых данных. Во-вторых, если потребность в каких-то данных отпала до окончания программы, то занятую ими память можно освободить для другой информации. В-третьих, использование динамической памяти позволяет создавать структуры данных переменного размера.
57. Библиотеки программ и классов. Статические и динамические библиотеки. Критерии проектирования библиотек.
Библиотека(от англ. library) в программировании — сборник подпрограмм или объектов, используемых для разработки программного обеспечения (ПО).
В некоторых языках программирования[каких?] то же, что модуль, в некоторых — несколько модулей. С точки зрения операционной системы (ОС) и прикладного ПО библиотеки разделяются на динамические и статические.
Динамические библиотеки
Часть основной программы, которая загружается в ОС по запросу работающей программы в ходе её выполнения (Run-time), то есть динамически (Dynamic Link Library, DLL в Windows). Один и тот же набор функций (подпрограмм) может быть использован сразу в нескольких работающих программах, из-за чего они имеют ещё одно название — библиотеки общего пользования (Shared Library). Если динамическая библиотека загружена в адресное пространство самой ОС (System Library), то единственная копия может быть использована множеством работающих с нею программ, что положительно сказывается на степени использовании ресурса ОЗУ. Динамические библиотеки могут содержать в себе как критические для работы программы части, так и дополнительные функции. Дополнительным плюсом такого подхода является то, что динамическая библиотека может быть использована в качестве плагина (Plug-ins), расширяющего функциональность программы. Минусом является то, что в случае, если модуль, который содержит в себе критическую часть, отсутствует, программа не сможет продолжить работу.
Динамические библиотеки хранятся обычно в определенном месте и имеют стандартное расширение. Например, файлы .library в логическом томе Libs: в AmigaOS; в Microsoft Windows и OS/2 файлы библиотек общего пользования имеют расширение .dll; в UNIX‐подобных ОС — обычно .so; в Mac OS — .dylib.
При написании программы программисту достаточно указать транслятору (компилятору или интерпретатору) языка программирования, что следует подключить нужную библиотеку и использовать функцию из неё. Ни исходный текст, ни исполняемый код функции в состав программы на данном этапе не входит.
Статические библиотеки
Могут быть в виде исходного текста, подключаемого программистом к своей программе на этапе написания (например, для языка Fortran существует огромное количество библиотек для решения разных задач именно в исходных текстах), либо в виде объектных файлов, присоединяемых (линкуемых) к исполняемой программе на этапе компиляции (в Microsoft Windows такие файлы имеют расширение .lib, в UNIX‐подобных ОС — обычно .a). В результате программа включает в себя все необходимые функции, что делает её автономной, но увеличивает размер. Без статических библиотек объектных модулей (файлов) невозможно использование большинства современных компилирующих языков и систем программирования: Fortran, Pascal, C, C++ и других.
58. Подпрограммы. Формальные и фактические параметры подпрограмм.
Подпрограммы – группы операторов, которые могут быть выполнены неоднократно. Её можно рассматривать как некий сегмент программы, который должен выполняться на разных стадиях вычисления, можно рассматривать, как логическую единицу декомпозиции программы. Классически подпрограмму делят на процедуры и функции.
Основные понятия
Интерфейс подпрограммы – указание ее имени, параметров и типа возвращаемого значения.
Имя подпрограммы – любой допустим идентификатор языка программирования, по которому транслятор однозначно восстанавливает соответствие между вызывающим и вызываемым кодом.
Формальный параметр – некоторое значение, в том числе переменная и адрес памяти передаваемая внутрь подпрограммы и использующаяся ею для вычисления, в том числе и дл возвращения значений.
Тело подпрограммы это группа операторов, из которых состоит программа.
Вызов подпрограммы – оператор, указывающий на необходимость выполнения подпрограммы. В большинстве языков программирования это указание имени и фактических параметров.
Фактический параметр – конкретное значение, подставляемое на место формального параметра.
• Подпрограмма - группа операторов, которая может быть выполнена неоднократно.
• Рассматривать можно как:
1. Сегмент программы, который должен выполняться на разных стадиях вычисления, может быть написан один раз в виде подпрограммы, а затем многократно выполняться.
2. Логическая единица декомпозиции программы( компиляции)
• Делятся на:
1. Функция ( записываются внутри выражений r*sin(angle))
2. Процедуры ( рассматриваются как атомарные операторы read(ch);)
• Функции - расширяют встроенные в язык операции, возвращают результат, который можно использовать в выражениях, как правило вызываются из выражений.
• Процедуры – они расширяют встроенные в язык операторы, просто выполняют действие, результат не возвращают, как правило вызываются отдельным оператором.
• В языке С используются только функции.
• Если функция не должна ничего возвращать то ее тип обозначается как void.
• В ранних языках программирования подпрограммы могли выполнять действия не имели имени и параметров.
• Описание (интерфейс) подпрограммы - указатели её имени, параметров и типа возвращаемого значения (если есть).
• Имя подпрограммы - любой допустимый идентификатор языка программирования, по которому транслятор однозначно восстанавливает соответствие между вызывающим и вызываемым кодом.
• Тело подпрограммы - группа операторов, из которых состоит подпрограмма.
• Вызов подпрограммы - оператор, указывающий на необходимость выполнения подпрограммы. В большинстве языков программирования это указатели имени (иногда предваряемое ключевым словом CALL) и фактических параметров.
Параметры подпрограмм
• Данные передаются подпрограмме в виде последовательности значений – параметры.
• Формальный параметр - некоторое значение (в т.ч переменная или адрес памяти), передаваемое внутрь подпрограммы и использующееся ею для вычислений( в т.ч и для возвращения значений)
• Объявление, которое находится в объявлении подпрограммы. Вычисление в теле подпрограммы пишется в терминах формальных параметров.
• Фактические параметры – конкретные значения, подставляемые на места формальных параметров подпрограммы. Это значение, которое вызывающая программа передает подпрограмме.
Int sq (int x)//х - формальный параметр
{Return x*x;}//в теле – формальные параметры
Sq(2);//вызов функции, ‘2’-фактический параметр
Различают три разновидности фактического параметра:
1. Локальный объект данных, принадлежащий вызывающей программе.
2. Нелокальный объект данных, видимый из вызывающей программы.
3. Результат вычислений функции, вызванной из вызывающей программы, который возвращается в точку вызова.
Преимущества подпрограмм
1. Уменьшение сложности программирования. Использование имени подпрограммы позволяет абстрагировать от деталей реализации, думать в терминах операций, относящихся к решаемой проблеме.
2. Закрытость реализации - модификация алгоритма внутри подпрограммы не воздействует на остальную программу.
3. Модульность программ - разбиение программы на небольшие куски, которые можно рассматривать отдельно, подпрограммы позволяют управлять более крупными программами.
4. Расширение возможностей языков программирования - создание библиотек. Мат функции синуса или логарифма обеспечиваются библиотекой, как и процедуры ввода-вывода.
59. Передача параметров подпрограмме.
Способы передачи параметров
1) Семантика COPY IN – копировать значение фактического параметра, в место памяти, выделенного для формального параметра. Особенности: функция возвращает только 1 результат, но если вычисления сложные, то может понадобиться возврат нескольких значений. Для того чтобы это осуществить нужно задать в процедуре несколько фактических параметров, которым могут быть присвоены результаты вычисления.
a. Целью выполнения подпрограммы может быть модификация данных, которые ей передаются, а не вычисления, тогда данная семантика не эффективна.
b. Параметр может быть настолько большим, что копировать его не эффективно.
2) Семантика COPY OUT - Фактический параметр должен быть переменный, а подпрограмме передается адрес фактического параметра, которая она сохраняет. Для формального параметра используется временная локальная переменная, и значение должно быть присвоено формальному параметру хотя бы один раз, во время выполнения подпрограммы. Когда выполнение подпрограммы завершено, значение копируется в переменную, для которой указывают сохраненный адрес.
3) Семантика COPY IN/OUT – фактически параметр копируется в подпрограмму, когда она вызывается. Результирующее значение копируется обратно после её завершения.
4) Семантика ссылки – передает адрес фактического параметра и обращается к параметру косвенно.
5) Формальные параметры являются внутренними элементами подпрограммы. Большинство языков программирования различают входные и выходные парамметры. Бывают параметры входные/выходные. Описание и использование параметров является специфичным для каждого языка программирования. В С все параметры являются входными, а для выходных используется передача указателя. Изменение входных параметров внутри подпрограммы никак не сказываются на фактических параметрах.
Поэтому в качестве фактических параметров, могут выступать любые выражения.
Выходные параметры наоборот нужны для того, чтобы их модификация отразилась на фактическом параметре.
Параметры подпрограммы имеют соответствующую область памяти. Они недоступны вне этих подпрограмм.
Глобальные переменные программы доступны внутри подпрограмм только в случае, если никакой параметр подпрограммы не имеет такое же имя.
При вызове подпрограммы входные параметры передаются через внутреннюю память (стек).
Значение фактических параметров копируется в создаваемой ячейке локальных переменных, из-за этого вызов подпрограмм с входными параметрами производятся медленнее и потребляют дополнительную память. Однако, изменение типа параметра с входного на входной и выходной или использование глобальных переменных нарушает структурированность программы. Возвращаемый функцией результат является специальной выходной переменной. Большинство языков программирования поддерживают строгую типизацию параметров подпрограмм. Так же во многих языках программирования разрешается создавать вложенные подпрограммы.
<Методы передачи параметров>
• Сопоставление между фактическими и формальными параметрами в вызове подпрограммы - передача параметров.
• Методы передачи параметров – способы, которые параметры передаются в/из подпрограммы.
• Два способа, которые применяются для установления такого связывания:
1. Позиционное сопоставление.
2. Сопоставление по имени.
Позиционное сопоставление
• Соответствие устанавливаются на основе их позиций в списках фактических и формальных параметров - два параметра, которые занимают одинаковые позиции списках, образуют пару.
• Обычно количество формальных и фактических параметров должны обладать, чтобы соответствие между ними было взаимно однозначным.
• Питон, Ruby, php, с++ формальные параметры могут иметь значения по умолчанию.
• Используется если формальному параметру не передается никакого фактического параметра.
Питон:
Def salary(hours, tax_freee=1, hour_rate)//tax_free задан
My_money=salary(120, hour_rate=10.0)//можно вызвать так
Сопоставление по имени
Можно явно указать, какой формальныйпараметр должен соответствовать данному фактическому параметру.
Недостаток - пользователь подпрограммы должен знать имена формальных параметров.
Питон:
Adder(size=the_size, #size, list, sum - формальные
List=the_list, #the size, the list, the sum – фактические ( конкретные значения)
Sum=the_sum)
Методы передачи параметров: