Область действия переменной — часть программы, в которой объект существует. Отвечает на вопрос «где объект есть», а не «где его можно назвать по имени» — второе называется областью видимости.
Не из лекции
Английского оригинала в материалах нет. Ближайшие понятия стандарта — lifetime (время жизни объекта) и storage duration (длительность хранения). В части русской литературы «областью действия» называют как раз scope — при чтении других источников стоит проверять, что имеется в виду.
Зачем нужно
У переменной есть не только тип и значение. Важно ещё, где она существует и где к ней можно обратиться по имени. На маленьких программах это кажется очевидным, но как только появляются вложенные блоки, одинаковые имена и глобальные переменные, без этих правил код превращается в угадайку.
Как работает
Автоматические переменные
Обычная локальная переменная, объявленная внутри функции, действует с места объявления до ближайшего конца блока, в котором объявлена. Такие переменные называются автоматическими:
int main() {
int x;
{
float y;
{
bool b;
{
char c;
} // здесь заканчивается действие c
} // здесь заканчивается действие b
} // здесь заканчивается действие y
} // здесь заканчивается действие x«Автоматическая» — потому что программист не пишет отдельную команду освобождения памяти. Объект появляется при входе в нужную часть программы и исчезает при выходе из неё.
Блок — составной оператор в фигурных скобках, см. Выражения и операторы. Именно он задаёт границы: после закрывающей скобки переменной уже нет.
int main() {
int x = 1;
{
int y = 2;
std::cout << x + 3 * y << '\n';
} // здесь y уже уничтожена
return y; // ошибка: 'y' was not declared in this scope
}Глобальные переменные
Переменная, объявленная вне функций, называется глобальной. Она существует всё время работы программы:
int x = 1;
int main() {
int y = 2;
std::cout << x + 3 * y << '\n';
return 0;
}Но существовать всё время работы — не то же самое, что быть доступной отовсюду.
Имя должно быть объявлено до использования: глобальная переменная, определённая
ниже main, внутри main не видна, пока её не объявить через extern —
см. Объявление и определение.
Переменные в заголовках циклов и условий
Имя можно объявить прямо в if, while или for. У for область действия
сущности, объявленной в init, совпадает с областью самого оператора for —
после цикла счётчика уже нет. Подробнее — Циклы.
Тонкости
-
Область видимости не бывает больше области действия: нельзя увидеть по имени объект, который уже не существует. Меньше — бывает: глобальная
xсуществует, пока выполняетсяmain, но её имя может скрыть локальнаяx. -
Блок можно поставить просто так, без
ifи цикла, чтобы ограничить время жизни временной переменной:int answer = 0; { int temporary = 40 + 2; answer = temporary; } // temporary здесь уже нетЧем меньше часть программы, в которой видна переменная, тем проще понять, где её могут изменить.
Частые ошибки
- Объявить переменную внутри блока
ifили цикла и попытаться прочитать её после блока. - Считать, что раз глобальная переменная существует всю программу, к ней можно обратиться из любой строки файла.
Углубление — L03
L03 называет области памяти: глобальная (статическая), автоматическая (стековая), динамическая и потоковая (её в курсе не разбирают). Глобальные и автоматические переменные из этой заметки живут в первых двух.
У них два ограничения: время жизни жёстко фиксировано — до конца программы или
до конца блока, — а размер обычно ограничен несколькими мегабайтами. Третий
вариант времени жизни даёт динамическая память: объект
живёт от new до delete, независимо от блоков.
int* p = nullptr;
{
int x = 11;
p = new int(13);
}
std::cout << *p; // Ok: объект из new пережил блокКогда область действия объекта заканчивается раньше, чем область действия указателя на него, указатель становится висячим.
Углубление — L05
Локальную переменную можно объявить со словом static. Тогда у неё
статическая область действия (static storage duration), как у глобальной,
хотя имя видно только внутри функции. Из кода лекции, статические переменные:
- инициализируются один раз;
- сохраняют значение между вызовами;
- по умолчанию инициализируются нулями.
int& G() {
static int x; // = 0
return ++x;
}
G(); // 1
G(); // 2
G(); // 3x не умирает в конце вызова, поэтому G может вернуть на неё ссылку — в
отличие от обычной локальной переменной, см. Висячий указатель.
static работает и во вложенном блоке: в функции H из лекции x и z
создаются при каждом вызове, а static int y одна и растёт 1, 2, 3.
Не из лекции
Обнуление по умолчанию — свойство статической области действия. Обычная локальная
int x;нулём не становится, см. Инициализация переменных.