Подпрограммы

Содержание

Слайд 2

Понятие о подпрограммах При решении задач довольно часто встречается ситуация, когда

Понятие о подпрограммах

При решении задач довольно часто встречается ситуация, когда приходится

многократно повторять одни и те же группы операторов, или требуется выполнить одни и те же вычисления, но с различными данными. Чтобы облегчить процесс программирования таких задач, в языки программирования было введено понятие подпрограмм.
Кроме того, подпрограммы удобно использовать для выделения логически завершённого участка кода, доже если он выполняется только один раз.
Слайд 3

Определение подпрограммы Подпрограмма – это именованная группа операторов, оформленная специальным образом,

Определение подпрограммы

Подпрограмма – это именованная группа операторов, оформленная специальным образом, которая

может вызываться по имени.
При вызове подпрограммы ей передаётся управление вычислительным процессом.
После выполнения подпрограммы осуществляется возврат на оператор вызывающей программы, следующий за вызовом подпрограммы. Такой процесс называется выходом из подпрограммы.
Слайд 4

Процедуры и функции Различают два вида подпрограмм: процедуры; функции. Функции в

Процедуры и функции

Различают два вида подпрограмм:
процедуры;
функции.
Функции в момент выхода возвращают значение

определённого типа (результат вызова), которое может быть использовано в вызывающей программе. Вызов функций, как правило, выполняется в выражениях.
Процедуры ничего не возвращают. Их вызов – отдельный оператор.
Функция может быть вызвана, как процедура (её результат теряется), но не наоборот.
Слайд 5

Функции в C++ В языке C++ нет понятия процедур, есть только

Функции в C++

В языке C++ нет понятия процедур, есть только функции.

Однако, если функция возвращает специальный тип void, она по сути является процедурой.
Функции могут быть объявлены и определены.
объявление_функции ::= заголовок;
определение_функции ::= заголовок {операторы ... }
заголовок ::= [модификаторы] тип_возврата имя_функции
([формальные_параметры])
формальный_параметр ::= [const] тип имя [= значение_по_умолчанию]
вызов_функции ::= имя_функции (фактические_параметры)
Слайд 6

Формальные и фактические параметры Формальные параметры, описанные в заголовке функции, играют

Формальные и фактические параметры

Формальные параметры, описанные в заголовке функции, играют роль

дополнительных локальных переменных. Значения формальных параметров формируются при вызове функции, исходя из вычисленных значений фактических параметров.
При вызове функции фактический параметр преобразуется к типу формального.
Если формальный параметр описан с модификатором const, изменять его значения нельзя!
Если последние из фактических параметров не заданы, то соответствующие формальные параметры инициализируются значениями по умолчанию.
Слайд 7

Пример определения и вызова функции Задача: написать функцию, возвращающую максимальное из

Пример определения и вызова функции

Задача: написать функцию, возвращающую максимальное из трёх

чисел. Числа передаются в функцию через параметры.
int max3(int a, int b, int c){ //заголовок функции
int max;
if (a > b)
max = a;
else
max = b;
if (c > max)
max = c;
return max;
} //конец тела функции
Вызов этой функции в головной программе может выглядеть так:
int m, x=67, y=98, z=56;
cout << max3(x, y, z+50)<< endl; //106
cout << max3(9, 7, 111)<< endl; //111
Слайд 8

Выход из функции Выход из функции может быть выполнен двумя способами:

Выход из функции

Выход из функции может быть выполнен двумя способами:
выполнением специального

оператора
return [выражение]
после выполнения последнего оператора, входящего в тело функции (допускается только для функций, возвращающих void, и для функции main)
Допускается запись нескольких операторов return, задающих выход из разных ветвей вычислений.
Слайд 9

Способы передачи параметров Различают две реализации передачи параметров в функцию: по

Способы передачи параметров

Различают две реализации передачи параметров в функцию:
по значению;
по адресу
При

передаче параметра по значению перед вызовом функции выделяется специальный участок памяти, в которую заносится значение фактического параметра. При выходе из функции эта память освобождается. Изменения формальных параметров, переданных по значению, теряются при выходе!
При передаче параметров по адресу функция получает адрес тех данных, с которыми работает также и головная программа. Изменения сохраняются после выхода!
Слайд 10

Схема организации передачи параметров Головная программа Вызываемая функция Параметр Головная программа

Схема организации передачи параметров

Головная программа

Вызываемая функция

Параметр

Головная программа

Вызываемая функция

Параметр

Передача параметров по значению

Передача

параметров по адресу
Слайд 11

Схема организации передачи параметров (продолжение) Передача выходных параметров (не поддерживается в

Схема организации передачи параметров (продолжение)

Передача выходных параметров
(не поддерживается в C++)

Передача константных

параметров по адресу
Слайд 12

Особенности передачи параметров в C и C++ В языках C и

Особенности передачи параметров в C и C++

В языках C и C++

параметры передаются только по значению.
Преимущество: в качестве фактического параметра может быть записано любое выражение подходящего типа.
Недостаток: изменения формальных параметров теряются при выходе из вызываемой подпрограммы.
Слайд 13

Пример неправильной работы с параметрами Задача: написать функцию, меняющую местами значения

Пример неправильной работы с параметрами

Задача: написать функцию, меняющую местами значения своих

параметров.
void Swap (int a, int b) { int tmp = a; a = b; b = tmp; }
int x=10, y=100;
Swap(x,y);
// обмена нет!
Слайд 14

Решение проблемы модификации параметров в C Если нам необходимо изменить значение

Решение проблемы модификации параметров в C

Если нам необходимо изменить значение переданных

в функцию данных, в качестве формального параметра должен быть задан указатель, а в качестве фактического – адресное выражение:
void Swap (int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }
int x=10, y=100;
Swap(&x, &y);
// обмен выполнен!
Слайд 15

Ссылочные типы C++ Ссылочный тип появился в языке C++ и используется

Ссылочные типы C++

Ссылочный тип появился в языке C++ и используется главным

образом при работе с модифицируемыми параметрами функций.
Ссылочный тип, как и указатель, основан на некотором базовом типе, его описание имеет вид: базовый_тип&
Ссылку можно рассматривать как синоним имени, указанного при ее инициализации, или как указатель, который автоматически разыменовывается.
Ссылки в C++ должны быть связаны с каким-либо объектом. Таким образом, «нулевые ссылки» ( и тип void&) в C++ отсутствуют.
Слайд 16

Примеры работы со ссылочным типом int c = 100; int &p

Примеры работы со ссылочным типом

int c = 100;
int &p = c;


// p и c – различные имена для одной переменной
int &r; // ошибка – ссылка не может быть пустой
р++; // переменная с также будет равна 101
Слайд 17

Использование ссылочного типа для передачи параметров Ссылочный тип может использоваться как

Использование ссылочного типа для передачи параметров

Ссылочный тип может использоваться как в

описании формальных параметров, так и при задании типа возвращаемой функции.
Если формальный параметр – ссылка, то фактическим параметром должно быть Lvalue- выражение соответствующего типа. Модификации фактических параметров сохраняются после выхода из функции;
Если функция возвращает ссылку, то результат её работы – Lvalue- выражение.
Слайд 18

Пример использования ссылки в качестве параметра Задача: написать функцию, меняющую местами

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

Задача: написать функцию, меняющую местами значения

своих параметров.
void Swap (int &a, int &b) { int tmp = a; a = b; b = tmp; }
int x=10, y=100;
Swap(x,y);
// обмен выполнен!
Слайд 19

Пример использования ссылки при выходе int& my_inc(int &z) { z++; return

Пример использования ссылки при выходе

int& my_inc(int &z) {
z++;
return z;
}

int a =

1957;
my_inc(a) = 20;
cout << a << endl; // результат вывода - 20
Слайд 20

Константные ссылки Если требуется запретить изменение параметров во время выполнения функции

Константные ссылки

Если требуется запретить изменение параметров во время выполнения функции и

выдавать соответствующие сообщения на этапе компиляции, формальные параметры описываются с модификатором const. Ссылки также могут быть записаны с этим модификатором (т.н. константные ссылки)
Использование этого модификатора, во-первых, позволяет не строить громоздкие копии значений, а передавать лишь короткие адреса, и во-вторых не позволяет изменить значения фактических параметров.
Слайд 21

Пример константной ссылки void my_func(const long double &z) { cout return;

Пример константной ссылки

void my_func(const long double &z) {
cout << z <<

endl;
return;
}
void my_func(const long double z) {
cout << z << endl;
return;
}
В первом случае в стеке выделяется 4 байта, во втором – 10!
Если формальный параметр – константная ссылка, то фактический уже не обязан быть Lvalue!
Слайд 22

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

Побочный эффект подпрограмм

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

их изменения. Изменение программных объектов, не переданных в качестве параметров, называется побочным эффектом подпрограмм.
Преимущества побочного эффекта:
сокращается количество передаваемых параметров;
заголовок функции остаётся неизменным
Недостатки побочного эффекта:
можно случайно изменить глобальную переменную, предназначенную для совершенно других целей!
Слайд 23

Использование побочного эффекта void ReadData() { … // чтение данных и

Использование побочного эффекта

void ReadData() {

// чтение данных и инициализация глобальных переменных


}
Теперь ReadData() можно писать несколько раз, а изменение структуры данных и текста функции не влечёт изменение вызовов!
int k = 100;

void MyFunc () {

for (k=0; k<20; k++) { … }
}
В качестве параметра цикла используется глобальная переменная, и её значение изменяется!
Слайд 24

Рекурсия Под рекурсией понимается вызов подпрограммы из тела этой же подпрограммы.

Рекурсия

Под рекурсией понимается вызов подпрограммы из тела этой же подпрограммы. Подобные

соотношения достаточно часто встречаются в математике. Так, вычисление факториала можно представить следующим образом:
Для нахождения положительной степени числа можно использовать формулу
Такие соотношения называются рекуррентными.
Слайд 25

Примеры рекурсивных подпрограмм int Fact(int n) { if (n == 0)

Примеры рекурсивных подпрограмм

int Fact(int n) {
if (n == 0)
return

1;
else
return n * Fact(n-1);
}
double IntPower(double x; int n)
{
if (n == 0)
return 1;
else
return x * IntPower(x,n-1);
}
Слайд 26

Примеры рекурсивных подпрограмм (продолжение) Задача: ввести последовательность чисел, оканчивающуюся нулем и

Примеры рекурсивных подпрограмм (продолжение)

Задача: ввести последовательность чисел, оканчивающуюся нулем и

вывести их в обратном порядке.
void Reverse() { int ch; cin >> ch; if (ch != 0)
Reverse(); cout << ch << "\n";
}
Слайд 27

Формы рекурсивных подпрограмм Выполнение каких-то действий до рекурсивного вызова ( на

Формы рекурсивных подпрограмм

Выполнение каких-то действий до рекурсивного вызова ( на рекурсивном

спуске)
Выполнение каких-то действий после рекурсивного вызова ( на рекурсивном возврате)
Форма с выполнением действий как до, так и после рекурсивного вызова (с выполнением действий как на рекурсивном спуске, так и на рекурсивном возврате)
В общем случае любая рекурсивная функция включает в себя некоторое множество операторов S и один или несколько операторов рекурсивного вызова.
Слайд 28

Глубина и текущий уровень рекурсии Максимальное число рекурсивных вызовов функции без

Глубина и текущий уровень рекурсии

Максимальное число рекурсивных вызовов функции без возвратов,

которое происходит во время выполнения программы, называется глубиной рекурсии.
Число рекурсивных вызовов в каждый конкретный момент времени, называется текущим уровнем рекурсии.
Слайд 29

Зацикливание рекурсивных функций Рекурсивный вызов должен быть внутри какого-то условия, которое

Зацикливание рекурсивных функций

Рекурсивный вызов должен быть внутри какого-то условия, которое обязательно

должно быть ложным!
Зацикливание рекурсивных функций рано или поздно приведёт к ситуации «переполнение стека» (для каждой копии рекурсивной функции необходимо выделять дополнительную область памяти, а бесконечной памяти не существует).
Слайд 30

Пример зацикливающейся рекурсивной функции void Pech(){ cout } После компиляции выдаётся

Пример зацикливающейся рекурсивной функции

void Pech(){
cout << ”У попа была собака\n”; cout

<< ”Он её любил \n”; cout << ”Она съела кусок мяса -\n”; cout << ”Он её убил \n”; cout << ”Закопал, закопал \n”; cout << ”На могиле написал: \n”; Pech();
}
После компиляции выдаётся сообщение
warning C4717: 'Pech' : recursive on all control paths, function will cause runtime stack overflow
Максимальный уровень рекурсии - 4716
Слайд 31

Задача о ханойских башнях Даны три стержня A, B, C. На

Задача о ханойских башнях

Даны три стержня A, B, C. На стержне A находятся

n дисков разного диаметра, пронумерованных сверху вниз. Каждый меньший диск находится на большем. Требуется переместить эти диски на стержень C, сохранив их взаиморасположение. Перемещать можно только по одному верхнему диску и нельзя класть больший диск на меньший.
Слайд 32

Алгоритм решения задачи о ханойских башнях Если n=1 то 1. Переместить

Алгоритм решения задачи о ханойских башнях

Если n=1 то
1.  Переместить этот единственный диск

с A на C и остановиться.
иначе
2.  Переместить верхние n-1 дисков с A на В, используя С как вспомогательный.
3.  Переместить оставшийся нижний диск с A на C.
4.  Переместить n-1 дисков с В на С, используя А как вспомогательный.
Слайд 33

Неэффективность рекурсии Если рекурсивная программа содержит несколько рекурсивных вызовов с разной

Неэффективность рекурсии

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

то она будет работать медленно (для одного и того же уровня вычисления будут выполнены несколько раз).
Пример
Известную в комбинаторике величину «число сочетаний из n элементов по k» можно вычислять как по рекурсивной, так и по нерекурсивной формуле:
Слайд 34

Неэффективность рекурсии (продолжение) Первый вариант программы int sochet1(int n, int k)

Неэффективность рекурсии (продолжение)

Первый вариант программы
int sochet1(int n, int k) {
int

i, t, s;
if ((k==0)||(k==n))
return 1;
else {
if (n-k > k)
t = k;
else
t = n-k;
s = 1;
for (i = 1; i <= t; i++)
  s = s*(n-i+1))/i;
return s;
}
}
Слайд 35

Неэффективность рекурсии (продолжение) Второй вариант программы int sochet2(int n, int k)

Неэффективность рекурсии (продолжение)

Второй вариант программы
int sochet2(int n, int k) {
if

((k==0)||(k==n))
return 1;
else
return(sochet2(n-1,k)+
sochet2(n-1,k-1));
}
Слайд 36

Результаты вычислений для n=30, k=20

Результаты вычислений для n=30, k=20

Слайд 37

Перегрузка функций Часто удобно для функций, выполняющих один и тот же

Перегрузка функций

Часто удобно для функций, выполняющих один и тот же алгоритм

для данных разных типов, иметь одинаковые имена.
Создание нескольких различных функций с одинаковым именем, но с различными спецификациями (количеством и/или типом аргументов, но не типом возвращаемого значения) называется перегрузкой функций.
При вызове функции компилятор сам подбирает наиболее подходящий, по его мнению, вариант.
Если точного соответствия не найдено, то выполняется преобразование типов.
Слайд 38

Пример перегрузки функций int Min(int a, int b ) { return

Пример перегрузки функций

int Min(int a, int b ) {
return a

< b ? a : b;
}
Эта функция неприменима к типу double:
cout << Min(4.5, 6);
результат:
4
Добавляем перегруженные функции:
double Min(double a, double b ) {
return a < b ? a : b;
}
long long Min(long long a, long long b ) {
return a < b ? a : b;
}
и т.д.
Слайд 39

Пример перегрузки функций (продолжение) Для отдельных типов реализация перегруженной функции может

Пример перегрузки функций (продолжение)

Для отдельных типов реализация перегруженной функции может быть

совершенно другой:
const char* Min(const char* a, const char* b) {
return strcmp(a, b)<0 ? a : b;
}
Слайд 40

Пример перегрузки функций (окончание) Если компилятор не может подобрать подходящую функцию,

Пример перегрузки функций (окончание)

Если компилятор не может подобрать подходящую функцию, выдаётся

ошибка компиляции:
int a = 1957;
cout << Min(a+5, 42.8) << endl;
Сообщение:
error C2666: 'Min' : 3 overloads have similar conversions
d:\cpp\aab\prg.cpp(18): could be '__int64 Min(__int64,__int64)'
d:\cpp\aab\prg.cpp(15): or 'double Min(double,double)'
d:\cpp\aab\prg.cpp(11): or 'int Min(int,int)'
while trying to match the argument list '(int, double)'
Слайд 41

Указатели на функции Указатели на функции хранят адреса точек входа в

Указатели на функции

Указатели на функции хранят адреса точек входа в функции.

Операция разыменования, применённая к такому указателю, приводит к вызову функции.
Определение указателя:
типвозврата (* имя) ([параметры]);
Первые скобки нужны для изменения приоритета операций:
типвозврата * имя ([параметры]);
- объявление функции, возвращающей указатель
int (*pf) (int, int);
//определение указателя на функцию, возвращающую int
int *pf (int, int);
//объявление функции, возвращающей int *
Слайд 42

Пример использования указателей на функции int max(int a, int b) {

Пример использования указателей на функции

int max(int a, int b) {
return

(a>b ? a : b);
}

int (*pf) (int, int);
// это - указатель на функцию типа int с двумя
// параметрами

pf = &max;
pf = max; // можно писать и так… ☺

int s=*pf(5,10);
// вызов функции max через указатель на нее
int s=pf(5,10);
// можно писать и так… ☺
Слайд 43

Callback - функции Указатели на функции могут быть переданы в качестве

Callback - функции

Указатели на функции могут быть переданы в качестве параметров

в другие функции, что позволяет выполнять разные вызовы в зависимости от условий выполнения.
Функции, указатели на которые передаются в другие функции, и никогда не вызываемые напрямую, называются callback-функциями.
Слайд 44

Пример использования callback-функций int max(int a, int b) { return (a>b

Пример использования callback-функций

int max(int a, int b) {
return (a>b ?

a : b);
}
int min(int a, int b) {
return (a}
void PrintRezt (int f(int, int), int a, int b) {
cout << ”Result is: ” << f(a, b) << endl;
}

int (*maxmin) (int, int);

if (…) //надо выводить минимум
maxmin = min;
else
maxmin = max;

PrintRezt(maxmin, 20, 40);
Слайд 45

Функция main Функция main – та функция, с запуска которой начинает

Функция main

Функция main – та функция, с запуска которой начинает работу

консольное приложение, написанное на C++.
Форматы функции main:
void main(void);
int main(void);
void main(int argc, char *argv[]);
int main(int argc, char *argv[]);
// указаны традиционные имена для параметров
Первый и третий варианты не являются стандартными и не работают на некоторых платформах. Кроме того, писать void в скобках не обязательно. Таким образом, самое простое и легальное объявление функции main int main();
Слайд 46

Выход из функции main Если в функции main отсутствует оператор return,

Выход из функции main

Если в функции main отсутствует оператор return, то

перед выходом из неё подразумевается
return 0;
Это – требования стандарта. Однако это реализовано не во всех системах. Например, в VC++ 6.0 приходится явно записывать этот оператор.
Значение, возвращаемое функцией main, воспринимается операционной системой, как код завершения программы. Часто считается, что нормально завершившаяся программа выдаёт код 0, а ненулевой код говорит о проблемах при её работе.
Слайд 47

Передача параметров в функцию main Наличие параметров позволяет передать в запускаемую

Передача параметров в функцию main

Наличие параметров позволяет передать в запускаемую программу

данные командной строки: пользователь может при запуске программы указать несколько дополнительных параметров, разделяя их пробелами.
int main(int argc, char *argv[]);
Аргумент argc определяет, сколько параметров, включая имя программы, записано в командной строке. Массив нуль-терминированных строк argv хранит значение каждого параметра (argv[0] – имя exe-файла с полным путем к нему, argv[1] – первый параметр и т.д.).