Перейти к содержимому
Теория · Переменные и типы · урок 7 из 10

auto, область видимости и время жизни

Имя переменной, объявленной внутри фигурных скобок, действует только до закрывающей скобки. Снаружи его нет:

C++
#include <iostream>

int main() {
    int total = 0;
    {
        int bonus = 10;
        total = total + bonus;
    }
    std::cout << total + bonus << '\n';  // ✗
}
TEXT
error: use of undeclared identifier 'bonus'

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

Переменная, объявленная вне всех функций, — глобальная: её имя видно от объявления до конца файла, а живёт она до конца программы.

Сокрытие имени

Во вложенном блоке можно объявить переменную с тем же именем, что и снаружи. Это новая переменная: до конца блока имя обозначает её, а внешняя скрыта. Такое сокрытие (shadowing) заканчивается на закрывающей скобке, и внешняя переменная снова видна с прежним значением:

C++
#include <iostream>

int main() {
    int score = 5;
    {
        int score = 100;
        std::cout << score << '\n';  // 100
    }
    std::cout << score << '\n';  // 5
}

Время жизни

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

Переменная со словом static живёт до конца программы. Её инициализируют один раз, а не при каждом вызове, и между вызовами она хранит значение:

C++
#include <iostream>

void visit() {
    static int visits = 0;
    int steps = 0;
    visits = visits + 1;
    steps = steps + 1;
    std::cout << visits << ' ' << steps << '\n';
}

int main() {
    visit();  // 1 1
    visit();  // 2 1
    visit();  // 3 1
}

steps при каждом вызове начинается с 0, а visits продолжает счёт. static меняет только время жизни: видна visits по-прежнему лишь внутри visit.

Тип из начального значения

Вместо типа можно написать auto, и тогда компилятор выведет тип из начального значения. Он совпадает с типом литерала: auto count = 10; даёт int, а auto price = 2.5; — double. Для auto ratio = 2.5f; это float. Поэтому без начального значения auto не работает: auto total; не скомпилируется.

Частая ошибка

Переменную для дробной суммы объявили через auto с начальным значением 0:

C++
#include <iostream>

int main() {
    auto total = 0;  // ✗
    total = total + 2.5;
    std::cout << total << '\n';  // 2
}

Литерал 0 — это int, поэтому total тоже получает тип int. При присваивании 0 + 2.5 превращается в 2, а компилятор молчит. Исправление — литерал нужного типа: auto total = 0.0;.