Назад | Начало урока | Вперед
Содержание

Глава 15

Пришел, увидел, применил

В этой главе...

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

Вверх

Немного теории

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

Итак, разные переменные могут быть названы одинаковыми именами. До тех пор пока такие переменные используются разными функциями, они между собой не конфликтуют. Вы можете, например, определить переменную к для функции foo. А потом определить переменную к для функции baz. Хотя названия одинаковые, это разные переменные: одна используется только функцией foo, а вторая — только функцией baz.

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

Все переменные могут быть разделены на две категории: глобальные и локальные.

Глобальные переменные — это те, доступ к которым имеет любая функция программы (включая main). Они определяются за пределами какой-либо функции. Такие переменные нужны для тех случаев, когда некоторое значение должно быть доступным независимо от того, какая из функций в данный момент выполняется. Имена всех глобальных переменных должны быть разными.

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

Можно использовать только одну переменную с тем же именем внутри одной функции, однако этим же именем можно обозначать переменную, используемую в другой функции. (Изменение значения одной из этих переменных никак не отразится на значении другой переменной.)

Технические подробности:

На самом деле все несколько сложнее. Переменная видима в пределах пары фигурных скобок ({}). внутри которых она была определена. Так, если у вас есть переменная nBob, объявленная внутри функции First, и функция First вызывает функцию Second, переменная nBob не будет доступна для функции Second (если только вы не передадите этой функции переменную nBob в качестве аргумента). Точно так же, если вы определите переменную fYou внутри блока оператора if, например:

if(nBob > 10)
{

bool fYou = true ;
}

В этом случае переменная fYou будет видима только внутри данного блока.


Это очень удобно, и вот почему. Предположим, например, что у вас есть функция, отображающая числа от одного до десяти (пусть она называется CountUp). Чтобы сделать это, в ней используется цикл. В самом цикле используется переменная i для отсчета количества итераций. Если у вас есть другая функция, отображающая числа в обратном порядке — от десяти до одного (назовем ее CountDown), она также может использовать какую-то переменную для отсчета количества итераций. Поскольку обе переменные используются только в пределах своего цикла, вторую также можно назвать i. Это означает, что вам не нужно придумывать уникальные имена для переменных при написании каждого нового цикла. Если вы создаете большую программу, состоящую из огромного количества функций, часть из которых написана другими программистами, вам не придется кричать на всю комнату: "Эй! Кто-нибудь уже использовал для переменной цикла название sdbsdbsdbsdb3?"

Каждый раз, когда вы определяете переменную внутри какой-то функции, область ее
видимости будет ограничена пределами этой функции (т.е. она будет локальна). Вы можете использовать ее внутри этой функции, а также передавать в качестве аргумента другим подпрограммам, которые этой функцией вызываются. Как только функция заканчивает работу, локальная переменная прекращает свое существование. Использование переменных, которые передаются функциям в качестве аргументов, также ограничено пределами этих функций.

Вверх

Почему это так важно

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

int x ;
void ZeroIt(int x)
{

x = 0;
}
int main(void)
{
x = 1 ;
ZeroIt(x);
Console::WriteLine(x);
}

Что произойдет при выполнении этой программы? Вначале глобальной переменной х будет присвоено значение 1. Затем будет вызвана функция ZeroIt, которая присвоит этой переменной значение 0 (нуль). Как вы думаете, какое число будет отображено при выполнении инструкции Console::WriteLine? Отображено будет число 1.
Почему именно 1? Переменная х внутри функции ZeroIt является локальной по отношению к ней (ее область видимости ограничена пределами этой функции). Поэтому она создается, когда какая-то другая функция вызывает функцию ZeroIt. Этой переменной присваивается значение, которое передается функции в качестве аргумента (в данном случае это значение 1). Затем функция ZeroIt присваивает переменной х значение 0. Когда функция заканчивает работу, переменная х (имеющая значение 0 и являющаяся локальной по отношению к этой функции) уничтожается. Безвозвратно. Теперь компилятор возвращается к функции main и переходит к инструкции Console::WriteLine, которая отображает значение переменной х на экране. Но это уже другая переменная х — та, которая имеет глобальную область видимости. (Как вы помните, переменные, объявленные за пределами любой функции, являются глобальными.)

Точно такой же результат будет получен при выполнении кода, который приведен
ниже. Как и в предыдущем примере, изменение значения переменной х в процессе выполнения функции ZeroIt никак не отобразится на значении глобальной переменной х, которая используется функцией main. Причиной тому является локальная область видимости переменной х, которая используется внутри функции ZeroIt . Следовательно, эта переменная вместе со своим значением исчезает, как только ZeroIt заканчивает работу.

int x;

void ZeroIt(int x)
{

int x;
x = 0;
}

int main(void)
{

x = 1 ;
ZeroIt(x);
Console::WriteLine(x);
}

Совершенно другую картину мы наблюдаем в следующем примере:

int x;

void ZeroIt(int у)
{

у = 7;
х = 0;
}
int main(void)
{
х = 1;
ZeroIt(x);
Console::WriteLine(x) ;
}

Если будет выполнен этот код, на экране отобразится число 0 (нуль).
Почему теперь будет получен О? В данном случае аргумент функции ZeroIt обозначен именем у. Если вы передаете этой функции в качестве аргумента значение переменной х (глобальной переменной х, которая в данном примере своей локальной тезки не имеет), оно присваивается переменной у. Затем переменной у присваивается значение 7, а переменной х — значение 0. Поскольку для функции ZeroIt локальной переменной х не создавалось, это значение присваивается глобальной переменной х. Когда функция ZeroIt заканчивает работу, переменная у уничтожается. Поскольку х не является локальной по отношению к функции ZeroIt , она никуда не пропадает и сохраняет свое измененное значение.

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

Хорошие привычки

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

Вверх

Правила определения области видимости

К счастью, в C++ правила определения области видимости переменных довольно просты и могут быть легко перечислены,


Назад | Начало урока | Вверх | Вперед
Содержание