Переменная — это именованный участок памяти, в котором хранится значение, которое может быть изменено программой. Все переменные перед их использованием должны быть объявлены. Общая форма объявления[1] имеет такой вид:
тип список_переменных;
Здесь тип означает один из базовых или объявленных программистом типов (если необходимо — с одним или несколькими спецификаторами), а список_переменных состоит из одного или более идентификаторов, разделенных запятыми. Ниже приведены примеры объявлений:
int i,j,l; short int si; unsigned int ui; double balance, profit, loss;
Необходимо помнить, что в С имя переменной никогда не определяет ее тип.
Объявление переменных может быть расположено в трех местах: внутри функции, в определении параметров функции и вне всех функций. Это - места объявлений соответсвенно локальных, формальных параметров функций и глобальных переменных.
Переменные, объявленные внутри функций, называются локальными переменными. В некоторых книгах по С они называются динамическими переменными[2]. В этой книге используется более распространенный термин локальная переменная. Локальную переменную можно использовать только внутри блока, в котором она объявлена. Иными словами, локальная переменная невидима за пределами своего блока. (Блок программы — это описания и инструкции, объединенные в одну конструкцию путем заключения их в фигурные скобки.)
Локальные переменные существуют только во время выполнения программного блока, в котором они объявлены, создаются они при входе в блок, а разрушаются — при выходе из него. Более того, переменная, объявленная в одном блоке, не имеет никакого отношения к переменной с тем же именем, объявленной в другом блоке.
Чаще всего блоком программы, в котором объявлены локальные переменные, является функция. Рассмотрим, например, следующие две функции:
void func1(void) { int x; x = 10; } void func2(void) { int x; x = -199; }
Целая переменная х объявлена дважды: один раз в func1() и второй — в func2(). При этом переменная х в одной функции никак не связана и никак не влияет на переменную с тем же именем в другой функции. Это происходит потому, что локальная переменная видима только внутри блока, в котором она объявлена, за пределами этого блока она невидима.
В языке С есть ключевое слово auto (спецификатор класса памяти), которое можно использовать в объявлении локальной переменной. Однако так как по умолчанию предполагается, что все переменные, не являющиеся глобальными, являются динамическими, то ключевое слово auto почти никогда не используется, а поэтому в примерах в данной книге отсутствует.
Из соображений удобства и в силу устоявшейся традиции все локальные переменные функции чаще всего объявляются в самом начале функции, сразу после открывающейся фигурной скобки. Однако можно объявить локальную переменную и внутри блока программы (блок функции — это частный случай блока программы). Например:
void f(void) { int t; scanf("%d%*c", &t); if(t==1) { char s[80]; /* эта переменная создается только при входе в этот блок */ printf("Введите имя:"); gets(s); /* некоторые операторы ... */ } /* здесь переменная s невидима */ }
В этом примере локальная переменная s создается при входе в блок if и разрушается при выходе из него. Следовательно, переменная s видима только внутри блока if и не может быть использована ни в каких других местах, даже если они находятся внутри функции, содержащей этот блок.
Объявление переменных внутри блока программы помогает избежать нежелательных побочных эффектов. Переменная не существует вне блока, в котором она объявлена, следовательно, "посторонний" участок программы не сможет случайно изменить ее значение.
Если имена переменных, объявленных во внутреннем и внешнем (по отношению к нему) блоках совпадают, то переменная внутреннего блока "прячет" (т.е. скрывает, делает невидимой) переменную внешнего блока. Рассмотрим следующий пример:
#include <stdio.h> int main(void) { int x; x = 10; if(x == 10) { int x; /* эта x прячет внешнюю x */ x = 99; printf("Внутренняя x: %d\n", x); } printf("Внешняя x: %d\n", x); return 0; }
Результат выполнения программы следующий:
Внутренняя х: 99 Внешняя х: 10
В этом примере переменная х, объявленная внутри блока if, делает невидимой внешнюю переменную х. Следовательно, внутренняя и внешняя х — это два разных объекта. Когда блок заканчивается, внешняя х опять становится видимой.
В стандарте С89 все локальные переменные должны быть объявлены в начале блока, до любого выполнимого оператора. Например, следующая функция вызовет ошибку компиляции в С89:
/* Эта функция вызывает ошибку компиляции на компиляторе C89. */ void f(void) { int i; i = 10; int j; /* ошибка в этой строке */ j = 20; }
Однако в С99 (и в C++) эта функция вполне работоспособна, потому что в них локальная переменная может быть объявлена в любом месте внутри блока до ее первого использования.
Так как локальные переменные создаются и уничтожаются при каждом входе и выходе из блока, их значение теряется каждый раз, когда программа выходит из блока. Это необходимо учитывать при вызове функции. Локальная переменная создается при входе в функцию и разрушается при выходе из нее. Это значит, что локальная переменная не сохраняет свое значение в период между вызовами (однако можно дать указание компилятору сохранить значение локальной переменной, для этого нужно объявить ее с модификатором static).
По умолчанию локальные переменные хранятся в стеке. Стек — динамически изменяющаяся область памяти. Вот почему в общем случае локальные переменные не сохраняют свое значение в период между вызовами функций.
Локальные переменные можно инициализировать каким-либо заранее заданным значением. Это значение будет присвоено переменной каждый раз при входе в тот блок программы, в котором она объявлена. Например, следующая программа напечатает число 10 десять раз:
#include <stdio.h> void f(void); int main(void) { int i; for(i=0; i<10; i++) f(); return 0; } void f(void) { int j = 10; printf("%d ", j); j++; /* этот оператор не влияет на результат */ }
Если функция имеет аргументы, значит должны быть объявлены переменные, которые примут их значения. Эти переменные называются формальными параметрами функции. Внутри функции они фигурируют как обычные локальные переменные. Как показано в следующем фрагменте программы, они объявляются после имени функции внутри круглых скобок:
/* Возвращает 1, если в строке s содержится символ c, в противном случае возвращает 0 */ int is_in(char *s, char c) { while(*s) if(*s==c) return 1; else s++; return 0; }
Функция is_in() имеет два параметра: s и с, она возвращает 1, если символ, записанный в переменной с, входит в строку s, в противном случае она возвращает 0.
Внутри функции формальные параметры ничем не отличаются от обычных локальных переменных, единственное их отличие состоит в том, что при входе в функцию они получают значения аргументов. Можно, например, присваивать параметру какое-либо значение или использовать его в выражении. Необходимо помнить, что, как и локальные переменные, формальнее параметры тоже являются динамическими переменными и, следовательно, разрушаются при выходе из функции.
В отличие от локальных, глобальные переменные видимы и могут использоваться в любом месте программы. Они сохраняют свое значение на протяжении всей работы программы. Чтобы создать глобальную переменную, ее необходимо объявить за пределами функции. Глобальная переменная может быть использована в любом выражении, независимо от того, в каком блоке это выражение используется.
В следующем примере переменная count объявлена вне каких бы то ни было функций. Ее объявление расположено перед main(), однако, оно может находиться в любом месте перед первым использованием этой переменной, но только не внутри функции. Объявлять глобальные переменные рекомендуется в верхней части программы.
#include <stdio.h> int count; /* глобальная переменная count */ void func1(void); void func2(void); int main(void) { count = 100; func1(); return 0; } void func1(void) { int temp; temp = count; func2(); printf("count равно %d", count); /* напечатает 100 */ } void func2(void) { int count; for(count=1; count<10; count++) putchar('.'); }
Внимательно посмотрите на эту программу. Обратите внимание на то, что ни в func1(), ни в func2() нет объявления переменной count, однако они обе могут ее использовать. В func2() эта возможность не реализуется, так как в ней объявлена локальная переменная с тем же именем. Когда внутри func2() происходит обращение к переменной count, то это будет обращение к локальной, а не глобальной переменной. Таким образом, выполняется следующее правило: если локальная и глобальная переменные имеют одно и то же имя, то при обращении к ней внутри блока, в котором объявлена локальная переменная, происходит ссылка на локальную переменную, а на глобальную переменную это никак не влияет.
Глобальные переменные хранятся в отдельной фиксированной области памяти, созданной компилятором специально для этого. Глобальные переменные используются в тех случаях, когда разные функции программы используют одни и те же данные. Однако рекомендуется избегать излишнего использования глобальных переменных, потому что они занимают память в течение всего времени выполнения программы, а не только тогда, когда они необходимы. Кроме того, и это еще более важно, использование глобальной переменной делает функцию менее универсальной, потому что в этом случае функция использует нечто, определенное вне ее. К тому же большое количество глобальных переменных легко приводит к ошибкам в программе из-за нежелательных побочных эффектов. При увеличении размера программы серьезной проблемой становится случайное изменение значения переменной где-то в другой части программы, а когда глобальных переменных много, предотвратить это очень трудно.
[1]Называется также описанием или декларацией.
[2]А в книгах по C++ переменной автоматического класса памяти (т.е. такой, что создается при входе в блок, где она объявлена, и уничтожается при выходе из него).