Для приблизного розуміння локалізації змінних мабуть почнемо з алегорії. Що таке “локальність”? Це обмеженість місця дієздатності. Область видимості змінної – це та ділянка коду (функція, цикл, простір імені), в якому ця змінна оголошена (прописана). Поза цієї ділянки – компілятор її не бачить (вона недоступна).
Так і виходить – змінні описані наприклад всередині функцій, не можуть бути використані за її межами. Як приклад можна розглянути такий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <iostream> int i = 2; // глобальная переменная (видна в любом участке кода программы) int sum() { int k = 2; // локальная переменная (видна только внутри функции sum()) return i + k; } int main() { std::cout << i << std::endl << sum() << std::endl; } |
Давайте розберемо приклад ближче. Отже ми маємо змінну i, яка описана поза будь-якими функцій в програмі. Її область видимості і дії – вся програма без обмеження. А раз вона “законний” у всій програмі, у всіх її функціях і блоках операторів, укладених в {}, то її називають глобальної змінної.
Так само у нас в прикладі видно функція sum(), яка чогось робить. У ній всередині описана друга змінна – k. Вона вже має конкретну “прописку” – функція. При цьому дана змінна не може бути використана за межами функції. Тобто,. якщо в функції main() дописати ще й висновок цієї k на екран, компілятор почне “ругаться”. Він її просто напросто не бачить поза функцією sum(). перевірте:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <iostream> int i = 2; // глобальная переменная (видна в любом участке кода) int sum() { int k = 2; // локальная переменная (видна только внутри функции sum()) return i + k; } int main() { std::cout << i << std::endl << sum() << std::endl; std::cout << k << std::endl; // попытка обратиться к переменной k } |
Мінлива k називаєтьсялокальної, і її область видимості визначена відкриває і закриває фігурними дужками функції sum() – {…}. Далі них їй ходу немає, тому і використовувати її не можна поза цією функції. Компилируем:
Однак, потрібно також врахувати, що область видимості поширюється і на внутрішні блоки. Для прикладу можна розглянути такий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <iostream> int i = 2; int sum() { int k = 2; for (int i = 0; i < 10; i++) // эта i - локальная, объявленная в теле цикла k += 1; return i + k; // эта i - глобальная, объявленная вне функций } int main() { std::cout << i << std::endl << sum() << std::endl; } |
Тут виявлені наступні області видимості:
- Глобальна – i = 2 належить їй;
- Локальна щодо функції – переменная k;
- локальна щодо циклу for() – друга i.
Незважаючи на те, що в for є своя область видимості, сам for належить функції sum(). А значить він, і все що в ньому знаходиться, підпорядковується локальної області видимості цієї функції. Тобто,. всі змінні, визначені в функції так само дійсні і в тілі for, що і дозволяє працювати оператору k += 1 (він же к = до + 1 или k ).
Цікавіше справа йде зі змінною i, яка описана всередині for. Незважаючи на ім'я, ідентичне з глобальної змінної описаної вище, це вже інша змінна.
Як тільки цикл своє відпрацював, переменная i, описана в ньому, втрачає свої повноваження, або інакше кажучи – “звільняється” від обов'язків. Це не жарт – саме так і поступає менеджер пам'яті. Коли змінна описується, менеджер резервує під неї пам'ять, а коли її область видимості закінчується – пам'ять ця звільняється. Менеджер пам'яті у себе позначає, що ця ділянка пам'яті більш не належить кому-небудь, і його можна віддавати під запис інших даних.
Для закріплення ще один легкий прімерчік:
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include <iostream> int main() { int a = 0; { int a = 2; std::cout << a << std::endl; } std::cout << a << std::endl; } |
Тут область видимості описана блоками операторів, які обмежуються{}, Тобто,. перший cout виведе 2, тому що визначений блок операторів, в якому спрацює цеcout. І в цьому ж блоці визначена локальна змінна з ім'ям а, з неї він і візьме значення перш за все.
А вже за межами {} оператор coutвиведе 0, оскільки для нього не існує тієї а, що дорівнювала 2. Його область видимості збігається з іншого а, яка дорівнює 0, і визначена вище вкладеного блоку.
взагалі запам'ятайте – в реальному житті ніколи не треба давати однакові імена змінним (за винятком тих, що класично використовують для лічильників циклів. Наприклад i, j, k. Якщо у вас 10 циклів в коді і вони не вкладені, то для кожного з них можна оголошувати і визначати лічильник з ім'ямi. Це нормально). В других случаях, завжди давайте змінним унікальні імена, якщо не хочете, щоб вас потім “згадували” програмісти, які будуть розбиратися в вашому коді.
Пропонуємо також переглянути відео-урок по темі область видимості.