Если в таких языках, как Алгол, Фортран, ПЛ/1, Паскаль и др. делает различие между программами, подпрограммами, процедурами, функциями, то в языке Си++ и в его предшественнике - в языке Си -используются только функции.
При программировании на языке Си++ функция - это основное понятие, без которого невозможно обойтись. Во-первых, каждая программа обязательно должна включать единственную функцию с именем main (главная функция). Именно функция main обеспечивает создание точки входа в откомпилированную программу. Кроме функции с именем main, в программу может входить произвольное количество неглавных функций, выполнение которых инициируется прямо или опосредованно вызовами из функции main. Всем именам функций программы по умолчанию присваивается класс памяти extern, т.е. каждая функция имеет внешний тип компоновки и статическую продолжительность существования. Как объект с классом памяти extern, каждая функция глобальна, т.е. при определенных условиях доступна в модуле и даже во всех модулях программы. Для доступности в модуле функция должна быть в нем определена или описана до первого вызова.
Итак, каждая программа на языке Си++ - это совокупность функций, каждая из которых должна быть определена или, по крайней мере, описана, до ее использования в конкретном модуле программы. В определении функции указываются последовательность действий, выполняемых при ее вызове, имя функции, тип функции (тип возвращаемого ею значения, т.е. тип результата) и совокупность формальных параметров (аргументов). Каждый формальный параметр не только перечисляется, но и специфицируется, т.е. для него задается тип. Совокупность формальных параметров определяет сигнатуру функции. Этот термин активно используется в связи с перегрузкой функций. Сигнатура функции зависит от количества параметров, от их типов и от порядка их размещения в спецификации формальных параметров.
Определение функции, в котором выделяются две части - заголовок и тело, имеет следующий формат:
тип_функции имя_функции (спецификация_формальных_параметров) тело_функции
Здесь тип_функции - тип возвращаемого функцией значения, в том числе void, если функция никакого значения не возвращает. имя_функции - идентификатор. Имена функций как имена внешние (тип extern) должны быть уникальными среди других имен из модулей, в которых используются функции. Спецификация формальных параметров - это либо пусто, либо void, либо список спецификаций отдельных параметров, в конце которого может быть поставлено многоточие. Спецификация каждого параметра в определении функции имеет вид:
тип [имя_параметра] [= умалчиваемое значение]
Как следует из формата, для параметра может быть задано (а может отсутствовать) умалчиваемое значение. И, более того, синтаксис языка разрешает параметры без имен, если последние не используются в теле функции. В проекте стандарта языка Си++ отмечается, что использование спецификации параметра без имени полезно для резервирования места в списке параметров. В дальнейшей этот параметр может быть введен в функции без изменения интерфейса, т.е. без изменения вызывающей программы. Такая возможность бывает удобной при развитии уже существующей программы за счет изменения входящих в нее функций.
Тело функции - это всегда блок или составной оператор, т.е. последовательность описаний и операторов, заключенная в фигурные скобки. Очень важным оператором тела функции является оператор возврата в точку вызова:
return [выражение];
выражение в операторе return определяет возвращаемое функцией значение. Именно это значение будет результатом обращения к функции. Тип возвращаемого значения определяется типом функции. Если функция не возвращает никакого значения, т.е. имеет тип void, то выражение в операторе return опускается. В этом случае необязателен и сам оператор return в теле функции. Необходимые коды команд возврата в точку вызова компилятор языка Си++ добавит в объектный модуль функции автоматически. В теле функции может быть и несколько операторов return. Оператор return можно использовать и в функций main. Если тип возвращаемого функцией main значения отличен от void, то это значение анализируется операционной системой. Принято, что при благоприятном завершении программы возвращается значение 0. В противном случае возвращаемое значение отлично от 0.
Даже в том случае, когда функция не должна выполнять никаких действий и не должна возвращать никаких значений, тело функции будет состоять из фигурных скобок { }. (Такая функция может потребоваться при отладке программы в качестве "заглушки")
Примеры определений функций с разными сигнатурами:
void print (char *name, int value) // Ничего не возвращает
{
cout << "\n" << name << value; // Нет оператора return
}
float min(float a, float b) // У функции два оператора возврата
{
if (а < b) return а; //Возвращает минимальное
return b; // из значений аргументов
}
float cube(float x) // Возвращает значение типа float
{
return х * x * x; // Возведение в куб вещественного числа
}
int max(int n, int m, int) // Вернет значение типа int
{
return n < m ? m : n;
}
При обращении к функции, формальные параметры заменяются фактическими, причем соблюдается строгое соответствие параметров по типам.
Согласование по типам между формальными и фактическими параметрами требует, чтобы в модуле до первого обращения к функции было помещено либо ее определение, либо ее описание (прототип), содержащее сведения о ее типе (о типе результата, т.е. возвращаемого значения) и о типах всех параметров. Именно наличие такого прототипа либо полного определения позволяет компилятору выполнять контроль соответствия типов параметров. Прототип (описание) функции может внешне почти полностью совпадать с заголовком ее определения:
тип_функции имя функции(спецификация_формальных_параметров);
Основное различие - точка с запятой в конце описания (прототипа). Второе отличие - необязательность имен формальных параметров в прототипе даже тогда, когда они есть в заголовке определения функции.
Приведем прототипы определенных выше функций:
void print(char *, int); // Опустили имена параметров
float min(float a, float b) ;
float cube(float x);
int max(int , int , int);
Обращение к функции (вызов функции) – это выражение с операцией “круглые скобки”. Операндами служат имя функции (либо указатель на функцию) и список фактических параметров:
имя_функции (список_фактических_параметров)
Значением выражения “вызов функции” является возвращаемое функцией значение, тип которого соответствует типу функции.
Фактический параметр функции – это в общем случае выражение. Соответствие между формальными и фактическими параметрами устанавливается по их взаимному расположению в списках. Фактические параметры передаются из вызывающей функции по значению, т.е. вычисляется значение каждого выражения, представляющего аргумент, и именно это значение используется в теле функции вместо соответствующего формального параметра. Таким образом список_фактических параметров – это либо пусто, либо void, либо разделенные запятыми фактические параметры.
При наличии прототипов вызываемые функции не обязаны размещаться в одном файле (модуле) с вызывающее функцией, а могут оформляться в виде отдельных модулей либо могут находиться уже в оттранслированном виде в библиотеке объектных модулей. Сказанное относится не только к функциям, которые готовит программист для включения в свою программу, но и к функциям из стандартных библиотек используемого компилятора. В последнем случае определения библиотечных функции, уже оттранслированные и оформленные в виде объектных модулей, находятся в библиотеке компилятора, а описания функций в виде прототипов необходимо включать в программу дополнительно. Обычно это делают с помощью препроцессорных команд
#include <имя файла>
Здесь имя_файла определяет текстовый (заголовочный) файл, содержащий прототипы той или иной группы стандартных для данного компилятора функций. Например, в текстах практически всех написанных нами программ присутствует команда
#include <iostream.h>
которая из файла с именем iostream.h включает в программу описания библиотечных классов и принадлежащих им функций для ввода и вывода данных. (Из всех средств, описанных в файле iostream.h, мы до сих пор использовали только объекты потокового ввода-вывода cout, cin и соответствующие им операции >>, <<)
Подобно тому, как это сделано в библиотеке стандартных функций компилятора, следует поступать и при разработке своих программ, состоящих из достаточно большого количества функций, размещенных в разных модулях. Прототипы функций и описания внешних объектов (переменных, массивов и т.д.) помещают в отдельный файл, который препроцессорной командой
#include "имя файла" включают в начало каждого из модулей программы. В отличие от библиотечных функций компилятора имя такого заголовочного файла записывается не в угловых скобках , а в кавычках. При этом не нужно беспокоиться об увеличении размеров создаваемой программы. Прототипы функций нужны только на этапе компиляции и не переносятся в объектный модуль, т.е. не увеличивают машинного кода. А прототипы тех функций, которые не вызываются в модуле, вообще не используются компилятором.