2. Переменные в функции
Все величины, описанные внутри функции, а также ее параметры, являются локальными. Областью их действия является функция. При вызове функции, как и при входе в любой блок, в стеке выделяется память под локальные автоматические переменные. Кроме того, в стеке сохраняется содержимое регистров процессора на момент, предшествующий вызову функции, и адрес возврата из функции для того, чтобы при выходе из нее можно было продолжить выполнение вызывающей функции.
Т.о., переменные, определенные в функции, имеют класс auto, они локальны, т.е. создаются при входе в функцию и уничтожаются при выходе из нее. Область видимости такой переменной – функция, к ней могут обращаться только операторы, принадлежащие определению функции.
Время жизни переменной, определенной в функции, - от входа до выхода из функции. Из этих качеств локальных переменных функции вытекает одно интересное свойство – в определениях разных функций переменным можно давать одинаковые имена.
Пример 3.
#include <stdio.h>
//определение функции father_age()
int father_age()
{ int age; //локальная переменная функции
printf (“\n Vvedite vozrast otza”);
scanf (“%d”,&age);
return (age);
}
//определение функции your_age()
int your_age()
{ int age; //локальная переменная функции
printf (“\n Vvedite vash vozrast”);
scanf (“%d”,&age);
return (age);
}
void main()
{//локальные переменные функции main()
int f_a, y_a, del;
f_a=father_age(); //вызовы функций
y_a=your_age();
del=f_a – y_a;
printf (“\n vash otez starshe vas na %d let \n ”, del);
}
При выходе из функции соответствующий участок стека освобождается, поэтому значения локальных переменных между вызовами одной и той же функции не сохраняются. Если этого требуется избежать, при объявлении локальных переменных используется модификатор static:
Пример 4. Использование статических переменных.
#include <iostream.h>
void f (int a)
{int m=0;
cout << “n m p \n”;
while (a--)
{static int n=0;
int p=0;
cout << n++ << ‘ ‘<< m++ << ‘ ‘<< p++ << ‘\n’;
}
}
int main()
{f(3);
f(2);
return 0;
}
Статические переменные в определении функции используются в том случае, если нужно сохранять результат вычислений между ее вызовами.
Статическая переменная n размещается в сегменте данных и инициализируется один раз при первом выполнении оператора, содержащего ее определение. Автоматическая переменная m инициализируется при каждом входе в функцию. Автоматическая переменная р инициализируется при каждом входе в блок цикла.
Программа выведет на экран:
n m p
0 0 0
1 1 0
2 2 0
n m p
3 0 0
4 1 0
При совместной работе функции должны обмениваться информацией. Это можно осуществить с помощью глобальных переменных, через параметры и через возвращаемое функцией значение.
Подобно статическим переменным, глобальные переменные создаются один раз в начале работы программы, по умолчанию инициализируются 0 и существуют до конца выполнения программы. Глобальные переменные видны во всех функциях, где не описаны локальные переменные с теми же именами, поэтому использовать их для передачи данных между функциями очень легко. Тем не менее это не рекомендуется, т.к. затрудняет отладку программы. Нужно стремиться к тому, чтобы функции были максимально независимы, а их интерфейс полностью определялся прототипом функции.
Пример 5. Использование внешних (глобальных) переменных.
Переменная х доступна как в функции main(), так и во всех других функциях программы.
…
//внешнее (глобальное) объявление переменной
int x=3;
//определение функции «увеличение х»
void plus1()
{int p=10; //локальная переменная
x=x+1;
p=p+1;
printf (“Plus 1: x=%d p=%d \n”, x, p);
}
//определение функции «уменьшение х»
void minus1()
{int m; //локальная переменная
x=x-1;
m=m-1;
printf (“minus 1: x=%d m=%d \n”, x, m);
}
//вызывающая программа
void main()
{printf (“nachalo: x=%d \n”, x);
x--;
p++; //ошибка, нет доступа
m++; //ошибка, нет доступа
plus1();
minus1();
minus1();
printf(“x = %d \n”, x);
}
К глобальной переменной х можно обращаться из любой функции программы, включая функцию main(). К переменным p и m доступа из функции main() нет, этих переменных просто не существует в момент выполнения функции main().
Обратим внимание, что локальную переменную р инициализировали прямо при создании и работа с ней не вызовет никаких проблем, переменную m оставили без инициализации и это приведет к получению непредсказуемого результата при выполнении инструкции m=m+1 и печати m. Инициализация локальных переменных – это не просто «правила хорошего тона», а совершенно обязательная вещь.
Если внешняя переменная (в примере это х) не инициализируется явно, то по соглашениям С в ней будет записан 0 (автоматически при трансляции программы). Это возможно сделать, т.к. внешние переменные создаются один раз на все время работы программы.
У локальных переменных иная природа, т.к. они создаются многократно, инициализировать их автоматически (про запас) слишком накладно. Поэтому под них просто отводится память с тем содержимом, которое в ней было до запуска программы. В результате в неинициализированных локальных переменных оказываются непредсказуемые значения, которые к тому же могут меняться от вызова к вызову, т.к. местоположение локальной переменной может меняться.