Тип_результата ID_функции(список параметров)
{
код функции
return выражение;
}
Рассмотрим составные части определения пользовательской функции.
Тип результата определяет тип выражения, значение которого возвращается в точку ее вызова при помощи оператора return выражение; (возврат). Выражение преобразуется к типу_результата, указанному в заголовке функции и передается в точку вызова. Тип возвращаемого функцией значения может быть любым базовым типом, а также указателем на массив или функцию. Если функция не должна возвращать значение, указывается тип void. В данном случае оператор return можно не ставить. Из функции, которая не описана как void, необходимо возвращать значение, используя оператор return. Если тип функции не указан, то по умолчанию устанавливается тип int.
Список параметров состоит из перечня типов и идентификаторов параметров, разделенных запятыми. Список параметров определяет объекты, которые требуется передать в функцию при ее вызове.
В определении и в объявлении одной и той же функции типы и порядок следования параметров должны совпадать. Тип возвращаемого значения и типы параметров совместно определяют тип функции.
Функция может не иметь параметров, но круглые скобки необходимы в любом случае. Если у функции отсутствует список параметров, то при декларации такой функции желательно в круглых скобках указать void. Например, void main(void){ ... }.
В функции может быть несколько операторов return, но может и не быть ни одного (тип void – это определяется потребностями алгоритма). В последнем случае возврат в вызывающую программу происходит после выполнения последнего оператора кода функции.
Пример функции, определяющей наименьшее значение из двух целочисленных переменных:
int min (int x, int y)
{
return (x<y) ? x : y;
}
Функции, возвращающие значение, желательно использовать в правой части выражений языка Си, иначе возвращаемый результат будет утерян.
В языке Си каждая функция – это отдельный блок программы, вход в который возможен только через вызов данной функции.
Вызов функции
Для вызова функции в простейшем случае нужно указать ее имя, за которым в круглых скобках через запятую перечислить список передаваемых ей аргументов. Вызов функции может находиться в любом месте программы, где по синтаксису допустимо выражение того типа, который формирует функция.
Простейший вызов функции имеет следующий формат:
ID_функции (список аргументов);
где в качестве аргументов можно использовать константы, переменные, выражения (их значения перед вызовом функции будут определены компилятором).
Аргументы в списке вызова должны совпадать со списком параметров вызываемой функции по количеству и порядку следования, а типы аргументов при передаче в функцию будут преобразованы, если это возможно, к типу соответствующих им параметров.
Связь между функциями осуществляется через аргументы и возвращаемые функциями значения. Ее можно осуществить также через внешние, глобальные переменные (см. гл. 12).
Глобальные переменные доступны всем функциям, где они не описаны как локальные переменные. Использовать их для передачи данных между функциями довольно просто, но тем не менее этого делать не рекомендуется. Необходимо стремиться к тому, чтобы функции в программе были максимально независимыми и чтобы их интерфейс полностью определялся прототипами этих функций.
Функции могут располагаться в исходном файле в любом порядке, при этом исходная программа может размещаться в нескольких файлах.
Все величины, описанные внутри функции, являются локальными. Областью их действия является функция. При вызове функции, как и при входе в любой блок, в стеке выделяется память под локальные автоматические переменные. Кроме того, в стеке сохраняется содержимое регистров процессора на момент, предшествующий вызову функции, и адрес возврата из функции, для того чтобы при выходе из нее можно было продолжить выполнение вызывающей функции. При выходе из функции соответствующий участок стека освобождается, поэтому значения локальных переменных между вызовами одной и той же функции не сохраняются. Если этого требуется избежать, при объявлении локальных переменных используется модификатор static, например:
#include <stdio.h>
void f1(int);
void main(void)
{
f1(5);
}
void f1(int i)
{
int m=0;
puts(" n m p ");
while (i--) {
static int n = 0;
int p = 0;
printf(" %d %d %d \n", n++ , m++ , p++);
}
}
Статическая переменная n будет создана в сегменте данных ОП и проинициализируется нулем только один раз при первом выполнении оператора, содержащего ее определение, т.е. при первом вызове функции f1. Автоматическая переменная m инициализируется при каждом входе в функцию. Автоматическая переменная р инициализируется при каждом входе в блок цикла.
В результате выполнения программы получим
n m p
0 0 0
1 1 0
2 2 0
3 3 0
4 4 0