Каждая переменная имеет определенную область видимости (scope). Область видимости представляет участок программы, в рамках которого можно использовать переменную.
Переменные бывают глобальными и локальными или автоматическими.
Локальные переменные определяются внутри блока кода (например, внутри функции) и существуют только в рамках этого блока. Эти переменные характеризуются автоматическим временем жизни: при входе в блок для этих переменных выделяется память (которую еще называют автоматическая память), а после завершения работы этого блока, выделенная память освобождается, а объекты удаляются.
#include <stdio.h> void printn() { int n = 63; printf("n=%d \n", n); } int main(void) { int a = 2; printn(); // n=63 //n++; так сделать нельзя, так как n определена в функции printn printf("a=%d \n", a); // a=2 return 0; }
Здесь в функции printn определена автоматическая переменная n. В функции main определена автоматическая переменная a. При вне своих функций переменные недоступны. Например, мы не можем использовать переменную n в функции main, так как ее область видимости ограничена функцией printn.
То же самое касается переменных, которые определены во вложенных блоках кода, например, условных конструкциях if..else, switch..case, циклах for,while, do..while. Например:
void printn() { { int n = 20; // локальная переменная printf("%d \n", n); } printf("%d \n", n); // Ошибка - здесь переменная n не видна }
Здесь переменная n
по прежнему является локальной и автоматической. Но она определена не просто в функции, а в блоке кода внутри функции. И вне этого блока кода даже внутри функции
ее нельзя использовать, потому что после завершения блока кода она удаляется.
Глобальные переменные определены в файле программы вне любой из функций и могут использоваться любой функцией из этого файла.
#include <stdio.h> int n = 5; void printn() { n++; printf("n=%d \n", n); } int main(void) { printn(); // n=6 n++; printf("n=%d \n", n); // n=7 return 0; }
Здесь переменная n является глобальной и доступна из любой функции. При этом любая функция может изменить ее значение.
Автоматические переменные, определенные внутри блока кода, могут скрывать внешние переменные с тем же именем:
#include <stdio.h> int n = 5; int main(void) { int n = 10; printf("n=%d \n", n); // n=10 { int n = 20; printf("n=%d \n", n); // n=20 } return 0; }
Здесь определено три переменных с именем n. Автоматическая переменная n, определенная на уровне функции main (int n = 10;
) скрывает
глобальную переменную n. А переменная n, определенная на уровне блока, скрывает переменную, определенную на уровне функции main.
Кроме глобальных и автоматических есть особый тип переменных - статические переменные. Они определяются на уровне функций с помощью ключевого слово static. Если автоматические переменные определяются и инициализируются при каждом входе в функцию, то статические переменные инициализируются только один раз, а при последующих вызовах функции используется старое значение статической переменной.
Например, пусть у нас будет функция со стандартной автоматической переменной:
#include <stdio.h> void display() { int i = 0; i++; printf("i=%d \n", i); } int main(void) { display(); display(); display(); return 0; }
Функция display вызывается три раза, и при каждом вызове программа повторно будет выделять память для переменной i, которая определена в функции. А после завершения работы display, память для переменной i будет освобождаться. Соответственно ее значение при каждом вызове будет неизменно:
i=1 i=1 i=1
Теперь сделаем переменную i статической:
#include <stdio.h> void display() { static int i = 0; i++; printf("i=%d \n", i); } int main(void) { display(); display(); display(); return 0; }
К переменной был добавлено ключевое слово static, поэтому при завершении работы функции display переменная не уничтожается, ее память не очищается, наоборот, она сохраняется в памяти. И соответственно результат работы программы будет иным:
i=1 i=2 i=3
Регистровые переменные фактически являются также автоматическими переменные, но при их определении используется ключевое слово register. Это слово сообщает компилятору, что данная переменная будет интенсивно использоваться в приложении, поэтому ее желательно поместить в регистр процессора, что увеличит быстродействие.
Ключевое слово register применяется к переменным:
register int x = 8;
И к параметрам функции:
void display(register int a) { printf("a=%d \n", a); }
Однако использование слова register еще не является гарантией, что переменная действительно будет помещена в регистр, так как много зависит от аппаратной части компьютера и установленных для нее ограничений. В то же время если аппаратные возможности не поддерживают регистровых переменных, то слово register просто будет игнорироваться, и его использование не вызовет ошибку.