C++ Программирование в среде С++ Builder 5

Область действия переменных и связанные с ней понятия


Теперь, когда мы более-менее разобрались с принципами функциональной организации программы, следует обсудить некоторые весьма важные вопросы относительно переменных.

Переменные в С могут быть локальными и глобальными, статическими и автоматическими, регистровыми, внешними и даже нестабильными. Они различаются своей областью действия, видимостью и временем жизни. Попробуем как-то сориентироваться во всем этом многообразии.

Область действия

Область действия — это та часть программы, где переменная в принципе доступна для программного кйда (что означает это “в принципе”, выяснится чуть позже). По своей области действия переменные делятся на локальные и глобальные.

Локальные переменные объявляются внутри функции и вне ее тела недоступны. Вернитесь к последнему примеру. Там программа состоит из двух функций. В main () объявляются переменные number, s и name (две последних — не простые переменные, а массивы, но это несущественно). В функции Convert объявлены grp1, grp2 и grp3.

Все эти переменные являются локальными. К каждой из них можно обращаться только в пределах объявляющей ее функции. Поэтому, кстати, имена локальных переменных не обязаны быть уникальными. Если две функции описывают переменную с одним и тем же именем, то это две совершенно различные переменные и никакой неоднозначности не возникает.

Параметры в определении функции (формальные параметры) можно рассматривать как локальные переменные, инициализируемые значениями аргументов при ее вызове.

В противоположность локальным глобальные переменные не относятся ни к какой функции и объявляются совершенно независимо. В пределах текущего модуля имя глобальной переменной, естественно, должно быть уникальным. Областью действия глобальных переменных является по умолчанию вся программа.

Довольно интересная проблема возникает, казалось бы, когда имя локальной переменной функции совпадает с именем некоторой глобальной переменной. Это вполне допустимая ситуация, и одноименные переменные здесь на самом деле различны. Если мы входим внутрь определения функции, то оказываемся в области действия сразу двух переменных. Однако локальная переменная в этом случае скрывает, как говорят, глобальную переменную с тем же именем. Тут речь идет о их области видимости, которая не совпадает с областью действия. Эти два понятия часто путают. Вот пример кода, иллюстрирующий вышесказанное.


/***************************************************



** Область действия и видимость переменных.

*/

#include <stdio.h>

int iVar = 111; // Глобальная переменная.

void Funci(void)

{

int iVar = 222; // Локальная переменная Funci().

/* Локальная переменная скрывает глобальную. */

printf("Значение iVar в Func1() равно %d.\n", iVar);

}

void Func2(void)

{

/* Глобальная переменная доступна. */

printf("Значение iVar в Func2 () равно %d.\n", iVar) ;

iVar = 333; // Изменяет глобальную переменную.

}

int main(void)

(

printf ("Начальное значение iVar: %d.\n", -iVar) ;

// Печатает 111. Funci (); // Печатает 222, но не изменяет

// глобальную iVar.

printf("После вызова Func1(): %d.\n", iVar) ;

Func2 (); // Печатает 111 и изменяет iVar на 333.

printf ("После вызова F'unc2(): %d.\n", iVar) ;

return 0;

}



Время жизни



Время жизни переменной в известной мере определяется ее областью действия. Память под глобальную переменную отводится, можно сказать, еще на этапе компиляции программы; во всяком случае, переменная существует все время, пока программа выполняется.

Локальная переменная создается при входе в функцию и уничтожается при возврате из нее. Она является автоматической переменной. Поэтому никак нельзя ожидать, например, что локальная переменная будет сохранять свое значение в промежутках между вызовами объявляющей ее функции. Память под переменную выделяется на стеке программы.

Если же вы хотите, чтобы локальная переменная сохраняла значение между вызовами функции, ее следует объявить с модификатором static, как в следующем:

int AFunc(void)

{

/* Так можно организовать счетчик вызовов функции. */

static int callCount = 0;

// Здесь что-то делается...

return ++callCount;

}



На тот случай, если у кого-то этот код вызвал сомнения, скажу, что инициализация локальной статической переменной производится всего один раз — при запуске программы, а не при каждом входе в функцию.

Такая переменная будет существовать все время, пока программа выполняется. Память под статическую переменную отводится в той же области, где располагаются глобальные переменные. Таким образом, статическая локальная переменная очень похожа на глобальную, за исключением того, что ее областью действия является все-таки объявляющая функция; вне ее переменная недоступна.





Модификаторы переменных



Помимо static, в С имеются и другие модификаторы, применяемые к объявлениям переменных. Опишем их вкратце.

  • static. Его воздействие на локальную переменную описано выше. Примененный к глобальной переменной, он ограничивает область ее действия текущим файлом (модулем компиляции).


  • auto. Специфицирует локальную переменную как создаваемую автоматически и подразумевается по умолчанию. Зачем он вообще нужен? Наверное, на всякий случай — для реализации языка, в которых все переменные по умолчанию статические. Никогда не видел, чтобы его кто-то применял.


  • register. Этот модификатор рекомендует компилятору разместить локальную переменную в регистре процессора, если это возможно.


  • extern. Модификатор говорит компилятору, что переменная является внешней, т. е. объявлена в другом файле.




  • Модификатор volatile



    Об этом модификаторе следует сказать отдельно. Он применяется для объявления переменных, которые можно назвать нестабильными. Модификатор volatile сообщает компилятору, что значение переменной может изменяться как бы само по себе, например, некоторым фоновым процессом или аппаратно. Поэтому компилятор не должен пытаться как-либо оптимизировать выражения, в которые входит переменная, — предполагая, например, что ее значение не менялось с предыдущего раза и потому выражение не нужно заново пересчитывать.

    Есть и другой момент. В программе могут быть так называемые критические участки кода, во время исполнения которых изменение значения нестабильной переменной приведет к абсурдным результатам. (Возьмите случай оценки “А или не-А”, если А нестабильно!) Для таких критических участков компилятор должен создавать копию, например, в регистре, и пользоваться этой копией, а не самой переменной.



    Можно написать такое объявление:

    volatile const int vciVar = 10;

    Другими словами, “нестабильная константа” типа int. В этом нет никакого противоречия — компилятор не позволит вашей программе изменять переменную, но и не будет предполагать ее значение известным априори, так как оно может меняться в силу внешних причин.


    Содержание раздела