Функция – это совокупность объявлений и операторов, предназначенных для выполнения отдельной задачи и заключённых в специальный блок. Необходимость в использовании функций возникает при решении сложных задач, когда нужно выполнять набор однотипных действий с различными данными. Любая программа на языке C должна содержать хотя бы одну функцию (функция main()). С использованием функций в языке C связаны три основные понятия: объявление, определение и вызов.
Определение функции – это описание действий, выполняемых функцией.
Форма записи:
модификатор1 тип модификатор2 имя (список_формальных_параметров)
{
тело_функции
}
Поле модификатор1 содержит спецификации класса памяти и является необязательным. Поле модификатор2 используется только в средах разработки фирмы Borland для изменения типа функции (cdecl или pascal) и является необязательным. Подробнее об этом – при рассмотрении классов памяти и модификаторов. Поле тип является обязательным и определяет тип возвращаемого функцией в вызывающую функцию значения. Если тип не указан явно, то по умолчанию считается, что функция возвращает значение типа int. Если функция не возвращает никакого значения, она должна иметь тип возвращаемого значения void. Функция может возвращать переменную, константу или указатель, но не может возвращать массив или другую функцию, для этого используются указатели. Поле имя является обязательным и определяет идентификатор функции. Поле список_формальных_параметров содержит объявления параметров функции, указанные через запятую, которые могут быть использованы в теле функции. Если функция не имеет параметров, то в данном поле указывается слово void. Формальные параметры – это локальные объекты (переменные, константы, структуры, объединения или указатели), существующие только в пределах тела функции и принимающие значения, переданные функции при вызове в соответствии с порядком следования их имён в списке параметров. Синтаксические правила объявления формальных параметров функции аналогичны синтаксическим правилам объявления соответствующих объектов. Тип формальных параметров может быть любым, но типы формальных параметров, указанных в определении функции, должны соответствовать типам формальных параметров, указанных в объявлении функци, и типам фактических параметров, передаваемых в тело функции при её вызове. Поле тело_функции является обязательным и задает последовательность операторов, выполняемых функцией.
Пример 1
void printf_bigger(int a,int b)
{
int c=a;
if(c<b)
c=b;
printf("%d",c);
}
При компиляции программы компилятор последовательно читает исходный код программы. Появление идентификатора функции (операция «вызов функции») в тексте программы приводит к генерации компилятором последовательности команд, обеспечивающей вызов функции (занесение в стек текущих параметров выполняемой программы, значений фактических параметров, передаваемых функции, и передачу управления на начальную команду тела функции). Чтобы функция могла быть вызвана, она должна быть объявлена или определена, поэтому объявление или определение функции должно всегда предшествовать ее вызову. Если определение функции находится в другом модуле или в том же модуле ниже, чем её вызов, для информирования компилятора может использоваться объявление функции, которое должно находиться раньше, чем вызов функции в этом модуле.
Объявление функции (задание прототипа функции) – это задание формы обращения к функции.
Форма записи:
модификатор1 тип модификатор2 имя (список_формальных_параметров);
Смысл полей модификатор1, модификатор2, тип и имя такой же, как и при определении функции. Поле список_формальных_параметров определяет количество и типы передаваемых функции параметров. В данном списке при объявлении функции достаточно указать только тип параметра, идентификатор параметра задавать не обязательно. Если всё же в объявлении функции используются идентификаторы параметров, то они не обязательно должны совпадать с идентификаторами параметров при определении функции, но строго должны совпадать их типы.
Пример 2
void printf_bigger(int,int h);
void main(void)
{
int i=2;
printf_bigger(i+1,3);
}
void printf_bigger(int a,int b)
{
int c=a;
if(c<b)
c=b;
printf("%d",c);
}
Определение функции будет одновременно являться и объявлением функции, если функция определяется до того, как будет вызвана.
Пример 3
void printf_bigger(int a,int b)
{
int c=a;
if(c<b)
c=b;
printf("%d",c);
}
void main(void)
{
int i=2;
printf_bigger(i+1,3);
}
Вызов функции – это операция передачи управления на первый оператор тела функции.
Форма записи:
операнд1(операнд2)
Поле операнд1 содержит адрес функции. Это либо указатель на функцию, либо идентификатор функции (который по своей природе также является указателем на функцию), либо выражение, тип значения которого – указатель на функцию. Поле операнд2 содержит список фактических параметров функции. Это один или несколько, разделенных запятой, объектов, доступных в вызывающей функции, которые имеют конкретные значения, передаваемые функции при вызове. Их отличие от формальных параметров в том, что последние не содержат конкретных значений и при каждом вызове функции принимают значения фактических параметров вызова.
Если функция возвращает какое-либо значение, то вызов функции может входить в другое выражение.
Вызов функции приводит к следующим действиям:
- вычисляется значение полей операнд1 (адрес функции) и операнд2, если типы фактических параметров не совпадают с типами формальных параметров в объявлении и определении функции, то производится неявное преобразование типов фактических параметров к типам формальных параметров, соответствие между фактическими и формальными параметрами задаётся порядком их следования в списке;
- в стек копируется адрес возврата из функции (адрес следующей за вызовом функции команды);
- значения фактических параметров копируются в ячейки памяти, предназначенные для хранения параметров функции (в стек);
- управление передаётся по адресу функции, который является первым оператором тела функции;
- последовательно выполняются операторы, составляющие тело функции, выполнение оператора return в теле функции возвращает управление в вызывающую функцию, при отсутствии оператора return управление возвращается после выполнения последнего оператора тела функции;
- при возврате из функции производится очистка стека: удаляются фактические параметры и адрес возврата, по которому передаётся управление в вызывающую функцию, если функция возвращает значение оператором return, то производится возврат значения в точку вызова;
- управление передаётся на следующую за вызовом функции команду.
Пример 4
printf_bigger(i+1,3)
Для возврата из функции используется оператор «return». В теле функции может быть несколько операторов «return». Форма записи:
return оператор
Оператор может быть пустым оператором или оператором «выражение».
Если оператор является пустым оператором, то возвращаемое значение функции отсутствует, функция просто передаёт управление в точку вызова. Это допустимо, если функция имеет тип возвращаемого значения void.
Если оператор является оператором «выражение», то возвращаемым значением функции будет значение данного выражения, которое неявно преобразуется к типу возвращаемого значения функции. Возвращаемое значение возвращается в точку вызова функции.
Если оператор «return» отсутствует, то функция работает до выполнения всех операторов, составляющих её тело, после чего управление передаётся в точку вызова. Это допустимо, если функция имеет тип возвращаемого значения void.
Пример 5
void printf_bigger(int a,int b)
{
int c=a;
if(c<b)
c=b;
return;
printf("%d",c);
}
void main(void)
{
int i=2;
printf_bigger(i+1,3);
}
Программа ничего не напечатает.
Пример 6
int printf_bigger(int a,int b)
{
return a>b?a:b;
}
void main(void)
{
int i=2;
printf("%d",printf_bigger(i,3)+2);
}
На экране будет напечатано: 5