
- •Тема 5 Функции
- •5.1 Синтаксис функций
- •5.2 Типизированные и нетипизированные функции. Оператор return
- •5.3 Автоматические и статические локальные переменные, глобальные переменные
- •5.4 Способы передачи параметров функциям
- •5.5 Передача массивов функциям
- •5.6 Функции специального назначения
- •5.6.1 Встроенные функции (inline-функции)
- •5.6.2 Рекурсивные функции
- •5.7.1 Аргументы функций по умолчанию
- •5.7.2 Перегрузка функций
- •5.7.3 Перегрузка функций и неопределенность
5.2 Типизированные и нетипизированные функции. Оператор return
Типизированной называется функция, которая по окончании своей работы возвращает значение, а нетипизированной (или void-функцией) – функция, которая ничего не возвращает.
Разница в вызове нетипизированной (void) и типизированной функции:
1. Вызов нетипизированной (void) функции выглядит как оператор:
void Func (int A); // объявление функции
void main( )
{
Func (10); // вызов – отдельный оператор программы
}
2. Вызов типизированной функции является операндом, т.е. частью любого допустимого выражения:
int Func (int A); // объявление функции
void main( )
{
int A, B, C;
C = Func (10); // вызов – часть оператора присваивания
C = A + B * Func (10); // вызов – часть арифметического выражения
if ( Func (10) < 100 ) . . . // вызов – часть оператора ветвления
while (Func (10) > 0 ) . . . // вызов – часть оператора цикла
}
Возврат из нетипизированной функции в вызвавшую ее функцию происходит при выполнении последнего оператора в теле функции или реализуется оператором:
return;
Возврат из типизированной функции реализуется оператором:
return выражение;
Выражение, указанное после return, неявно преобразуется к типу возвращаемого функцией значения и передается в точку вызова функции. Функция может содержать несколько операторов return. В этом случае они будут являться частью оператора (или операторов) ветвления, поэтому из нескольких операторов return всегда выполняется только один.
Примеры:
int f1()
{ . . . return 1; } //правильно
void f2()
{ . . .return 1; } //неправильно, f2 не должна возвращать значение
double f3()
{ . . .return 1; } //правильно, 1 преобразуется к типу double
ВНИМАНИЕ
Нельзя возвращать из функции указатель (или ссылку) на локальную переменную, поскольку по окончании работы функции она всё равно будет удалена.
Пример:
int* Func ()
{
int a = 5;
return &a; // нельзя!
}
5.3 Автоматические и статические локальные переменные, глобальные переменные
Все величины, описанные внутри функции, а также ее параметры, являются локальными. Областью их действия является функция.
При вызове функции, как и при входе в любой блок, в стеке выделяется память под локальные автоматические переменные. Они называются так, потому что создаются и удаляются автоматически.
При выходе из функции (или из блока) соответствующий участок стека освобождается, поэтому значения локальных переменных между вызовами одной и той же функции не сохраняются.
Если этого требуется избежать, при объявлении локальных переменных используется модификатор static:
void Func (int A) // определение функции
{
printf (“\n N M\n”);
while (A--)
{
static int N = 0;
int M = 0;
printf ( “ %d %d\n”, N++, M++);
}
}
void main( )
{
Func(4); //вызов функции
getch();
}
Статическая локальная переменная N размещается в сегменте данных и инициализируется только один раз при первом выполнении оператора, содержащего ее определение, а автоматическая локальная переменная M инициализируется нулём каждый раз при входе в блок. Программа выведет на экран:
N M
0 0
1 0
2 0
3 0
Статические локальные переменные можно использовать, например, для подсчёта количества вызовов функции.
При совместной работе функции должны обмениваться информацией. Это можно осуществить с помощью глобальных переменных, через параметры и через возвращаемое функцией значение.
Глобальные переменные объявляются, как правило, в начале программы вне всех функций и блоков и видны во всех функциях, где не описаны локальные переменные с теми же именами. Поэтому использовать их для передачи данных между функциями очень легко. Тем не менее, использовать этот способ не рекомендуется, поскольку это затрудняет отладку программы и препятствует помещению функций в библиотеки общего пользования.
Нужно стремиться к тому, чтобы функции были максимально независимы, а их интерфейс полностью определялся прототипом функции.