Когда алгоритм решаемой задачи несложен, и размер программы невелик, то она обычно пишется как одно целое. То есть текст программы состоит только из основной части — главной программы (на языке С++ — это функция main()). С ростом размера программы появляется необходимость разбивать решаемую задачу на подзадачи и оформлять их в виде отдельных частей (подпрограмм). В этом случае основная программа служит только для вызова этих подпрограмм.
Подпрограмма — это часть всей программы, оформленная особым образом. Как правило, в виде подпрограммы записывается какая-то логически завершённая часть программы. Активное использование подпрограмм при разработке программного обеспечения составляет основу модульного программирования.
Модульное программирование — такая технология программирования, когда алгоритм всей решаемой задачи разбивается на отдельные, логически завершенные части. Если эти части сложны для кодирования, то они снова разбиваются на более простые части. Этот процесс продолжается до тех пор, пока не получатся простые для программирования алгоритмы, предназначенные для реализации несложных действий. Основные принципы модульного программирования были сформулированы ещё в 60-х годах 20-го века.
Главное отличие подпрограммы от основной (главной) программы заключается в том, что управление может быть передано только главной программе. Подпрограмма может быть откомпилирована, но не может быть запущена на исполнение.
Преимущества от использования подпрограмм:
Возможность создания достаточно больших программ (ограничение — порядка 50000 строк. Разработка программ практически неограниченного размера требует применения классов. Речь о них пойдёт дальше).
Достаточно просто повторно использовать ранее написанный код.
Разработку отдельных частей программы, т.е. подпрограмм, можно поручить разным людям.
Сокращается срок разработки программы в целом за счёт повторного использования кода и благодаря возможности привлечения к программному проекту целой группы программистов.
Повышается надёжность программы, потому что подпрограммы, как правило, не велики, их можно досконально изучить, к тому же повторное использование в новых программах подпрограмм, которые уже применялись ранее и не давали «сбоев», повышает надёжность новой программы в целом.
В ряде случаев уменьшается размер программы: если, к примеру, для сортировки пяти массивов, что использованы в программе, применена одна и та же подпрограмма, а не пишется практически один и тот же код для каждого массива по отдельности, то экономия будет существенной. Конечно, если подпрограмма используется в программе только один раз, то размер программы в целом не только не сокращается, а наоборот, возрастает.
Недостатки от применения подпрограмм:
Использование подпрограмм всегда уменьшает скорость работы программы. Это становится заметным, когда размер подпрограммы слишком мал, например, один-два оператора.
Размер исходного кода и исполняемого модуля не всегда, но часто возрастает при использовании подпрограмм.
Грамотное применение подпрограмм требует более высокой квалификации от программиста, чем работа без подпрограмм.
Существует две категории подпрограмм: процедуры и функции.
Процедура — это подпрограмма, которая не возвращает через своё имя результата работы, поэтому она вызывается как отдельный оператор. Процедура «общается с внешним миром» через список параметров. Часть из них будут входными, часть — выходными, т.е. результатом работы.
Функция — это подпрограмма, которая через имя возвращает результат своей работы. Функция вызывается в выражении, а не как отдельный оператор. Через список параметров она может получить входные данные и вернуть результаты работы.
Традиционно подпрограмму оформляют в виде функции, если результатом работы является одиночный объект (число, символ, строка). Пример алгоритмов, которые удобно оформить функцией: вычисление значения определенного интеграла, нахождение минимального числа в массиве, подсчет количества пробелов в строке и т.д.
Если результатом работы подпрограммы является несколько объектов, или выходным данным является массив, то необходимо использовать процедуру, так как через имя подпрограммы можно вернуть только один простой объект (число, адрес, символ), а через список параметров — любое количество и одиночных и составных объектов. Возврат результата одновременно и через имя, и через список параметров плох, так как вводит пользователя подпрограммы в некоторое заблуждение относительно того, что может изменить подпрограмма.
Формально в языках С/С++ нет процедур, а имеются только функции. Если же необходима именно процедура, то достаточно создать функцию, возвращающую тип void.
Рассмотрим каким образом применить свою функцию в программе на языке С++. Чтобы использовать в программе свою собственную функцию, необходимо выполнить три действия:
задать прототип функции;
вызвать функцию в необходимом месте, например, в функции main()
дать определение функции.
Разберёмся немного подробнее с тем, что мы перечислили.
Прототип функции — это заголовок функции, который заканчивается точкой с запятой. Прототипы всех функций, используемых в программе, обычно записывают в начале текста программы до определения функций. Прототип необходим для того, чтобы у компилятора была полная информация о типе результата работы функции и о списке параметров, передаваемых в функцию. Когда в тексте программы встретится обращение к функции, то компилятор проверяет правильность её вызова, сверяясь с информацией из прототипа.
Для программиста прототипы тоже полезны: можно в сжатой форме сразу увидеть, какие функции используются в программе, какие параметры им необходимы и т.д.
Вызов функции возможен в любой функции, где это потребуется. Если тип результата функции — void, то вызов записывается отдельным оператором, в остальных случаях — в выражении того же типа, что и тип результата функции. В прочем, языки С/С++ позволяют записать вызов функции в виде отдельного оператора даже в том случае, когда она возвращает тип результата, отличного от void. К примеру, оператор
sin(x);
совершенно законен, хотя вряд ли стоит так делать.
Определение функции состоит из её заголовка и тела, записанного в виде блока. Допустимо записывать определение функций в любой последовательности. Нельзя определять функцию внутри другой функции (С/С++ — это не Паскаль!).
Теперь на конкретном примере рассмотрим применение своей функции.
Пример. Найти максимум из двух чисел. Алгоритм поиска максимума оформить в виде функции.
Возможный вариант программы:
#include <iostream>
using namespace std;
// 1)Прототип функции
double fmax(double a, double b);
int main()
{
double a = 4, b = 3, max;
cout << "a=" << a << " max=" << b << endl;
// 2)Вызов функции
max = fmax(a, b);
cout << "max=" << max << endl;
return 0;
}
// 3)Определение функции
double fmax(double x, double y)
{
double max;
if(x > y)
max = x;
else
max = y;
return max;
}
В работе с подпрограммами приходится иметь дело с двумя видами параметров: список фактических параметров и список формальных параметров.
Список параметров (фактических или формальных — всё равно) записывается в круглых скобках сразу за именем функции. В принципе, список параметров может быть пустым, но скобки за именем функции обязательны!
Список фактических параметров — это те реальные данные, с помощью которых можно настроить алгоритм подпрограммы на обработку конкретных данных. Фактические параметры указываются в списке параметров при вызове функции и передаются в эту функцию. В нашем примере при вызове функции fmax() в списке фактических параметров указаны две переменные: a и b. Это реальные объекты, под которые в функции main() выделена память, они могут иметь какие-то значения и т.д.
Список формальных параметров — это по сути набор требований, которые предъявляет подпрограмма к передаваемым в неё данным. Список фактических параметров записывается в заголовке функции при её определении в скобках. Для каждого параметра необходимо указать тип и имя. При необходимости задаётся и способ передачи (об этом позже — в следующей теме). Имена формальных параметров локализованы в подпрограмме и дополняют перечень локальных объектов этой подпрограммы. Так, в нашей функции fmax() формальные параметры x и y типа double дополняют список локальных переменных, состоящий из одной переменной max типа double. В итоге функция fmax() имеет три локальных переменных, известных только в этой функции.
Между списками фактических и формальных параметров должно выдерживаться полное соответствие по количеству параметров, их типу, порядку следования и способу передачи в функцию. Несоблюдение этих требований в лучшем случае (для программиста) приводит к ошибке на стадии компиляции, в худшем — к «багам» программы, которые будут время от времени проявляться во время выполнения программы. «Выловить» же такого рода ошибки очень непросто. Как правило, они остаются в программе в течение всего периода её использования. Поэтому исключительно важно соблюдать правильность передачи данных в подпрограмму.
|
|
|