Как мы уже знаем из предыдущих уроков, при выполнении процессором стейтмента int х;
создается переменная. Возникает вопрос: «Когда эта переменная уничтожается?».
Область видимости переменной определяет, кто может видеть и использовать переменную во время её существования. И параметры функции, и переменные, которые объявлены внутри функции, имеют локальную область видимости. Другими словами, эти параметры и переменные используются только внутри функции, в которой они объявлены. Локальные переменные создаются в точке объявления и уничтожаются, когда выходят из области видимости.
Рассмотрим следующую программу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <iostream> int add(int a, int b) // здесь создаются переменные a и b { // a и b можно видеть/использовать только внутри этой функции return a + b; } // здесь a и b выходят из области видимости и уничтожаются int main() { int x = 7; // здесь создается и инициализируется переменная x int y = 8; // здесь создается и инициализируется переменная y // x и y можно использовать только внутри функции main() std::cout << add(x, y) << std::endl; // вызов функции add() с a = x и b = y return 0; } // здесь x и y выходят из области видимости и уничтожаются |
Параметры a
и b
функции add() создаются при вызове этой функции, используются только внутри нее и уничтожаются по завершении выполнения этой функции.
Переменные x
и y
функции main() можно использовать только внутри main() и они также уничтожаются по завершении выполнения функции main().
Для лучшего понимания давайте детально разберем ход выполнения этой программы:
выполнение начинается с функции main();
создается переменная x
в функции main() и ей присваивается значение 7
;
создается переменная y
в функции main() и ей присваивается значение 8
;
вызывается функция аdd() с параметрами 7
и 8
;
создается переменная a
в функции add() и ей присваивается значение 7
;
создается переменная b
в функции add() и ей присваивается значение 8
;
выполняется операция сложения чисел 7
и 8
, результатом является значение 15
;
функция add() возвращает значение 15
обратно в caller (в функцию main());
переменные a
и b
функции add() уничтожаются;
функция main() выводит значение 15
на экран;
функция main() возвращает 0
в операционную систему;
переменные x
и y
функции main() уничтожаются.
Всё!
Обратите внимание, если бы функция add() вызывалась дважды, параметры a
и b
создавались и уничтожались бы также дважды. В программе с большим количеством функций, переменные создаются и уничтожаются часто.
Локальная область видимости предотвращает возникновение конфликтов имен
Из примера, приведенного выше, понятно, что переменные x
и y
отличаются от переменных a
и b
.
Теперь давайте рассмотрим следующую программу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> int add(int a, int b) // здесь создаются переменные a и b функции add() { return a + b; } // здесь a и b функции add() выходят из области видимости и уничтожаются int main() { int a = 7; // здесь создается переменная a функции main() int b = 8; // здесь создается переменная b функции main() std::cout << add(a, b) << std::endl; // значения переменных a и b функции main() копируются в переменные a и b функции add() return 0; } // здесь a и b функции main() выходят из области видимости и уничтожаются |
Здесь мы изменили имена переменных x
и y
функции main() на a
и b
. Программа по-прежнему работает корректно, несмотря на то, что функция add() также имеет переменные a
и b
. Почему это не вызывает конфликта имен? Дело в том, что a
и b
, принадлежащие функции main(), являются локальными переменными, функция add() не может их видеть, точно так же, как функция main() не может видеть переменные a
и b
, принадлежащие функции add(). Ни add(), ни main() не знают, что они имеют переменные с одинаковыми именами!
Это значительно снижает возможность возникновения конфликта имен. Любая функция не должна знать или заботиться о том, какие переменные находятся в другой функции. Это также предотвращает возникновение ситуаций, когда одни функции могут непреднамеренно (или намеренно) изменять значения переменных других функций.
Правило: Имена, которые используются внутри функции (включая параметры), доступны/видны только внутри этой же функции.
Тест
Каким будет результат выполнения следующей программы?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <iostream> void doMath(int a) { int b = 5; std::cout << "doMath: a = " << a << " and b = " << b << std::endl; a = 4; std::cout << "doMath: a = " << a << " and b = " << b << std::endl; } int main() { int a = 6; int b = 7; std::cout << "main: a = " << a << " and b = " << b << std::endl; doMath(a); std::cout << "main: a = " << a << " and b = " << b << std::endl; return 0; } |
Ответ
Результат выполнения программы:
main: a = 6 and b = 7
doMath: a = 6 and b = 5
doMath: a = 4 and b = 5
main: a = 6 and b = 7
Вот ход выполнения этой программы:
выполнение начинается с функции main();
создается переменная a
в функции main(), ей присваивается значение 6
;
создается переменная b
в функции main(), ей присваивается значение 7
;
cout выводит main: a = 6 and b = 7
;
вызывается doMath() с аргументом 6
;
создается переменная a
в функции doMath(), ей присваивается значение 6
;
выполняется инициализация переменной b
функции doMath() значением 5
;
cout выводит doMath: a = 6 and b = 5
;
переменной a
функции doMath() присваивается значение 4
;
cout выводит doMath: a = 4 and b = 5
;
переменные a
и b
функции doMath() уничтожаются;
cout выводит main: a = 6 and b = 7
;
функция main() возвращает 0
в операционную систему;
переменные a
и b
функции main() уничтожаются.
Обратите внимание, даже когда мы присвоили значения переменным a
и b
внутри функции doMath(), на переменные внутри функции main() это никак не повлияло.
Здравствуйте. Перечитал урок про тип возвратных функций и не понял всё равно. Если тип функции void не возвращает значения в caller, то как в этом случае функция с типом void выводит на экран значения?
Функция типа void может выводить данные на экран через стеймтенты cout — для этого ей ничего не нужно возвращать в caller.
Здраствуйте! Что такое caller, ранее несколько раз перечитывала уроки, но так и не поняла, можно примеры кода и из жизни. Благодарю!
caller — это функция, которая вызывает другую функцию. В уроке №12 об этом говорится — https://ravesli.com/urok-12-funktsii-i-return/#toc-0
Здравствуйте.
Материал отличны, легко и интересно читать.
Спасибо
Относительно последнего теста в уроке №15
Исходя из правила: "Имена, которые используются внутри функции (включая параметры), доступны/видны только внутри этой же функции."
Исходя из правила немного не понятно как и откуда переменная "а" получает значение "6". По моему пониманию значение все таки взято из main так называемого caller и передано в функцию doMath и присвоено опять же переменной "a". ведь в функции doMath до исполнения строки кода std::cout << "doMath: a = " << a << " and b = " << b << std::endl; значение переменной "а" определено не было.
Но вот что меня совсем запутало так это то что все происходит и самое главное компилируется без ошибок — так это то что функция doMath с типом возврата void.
Поясните пожалуйста для тех кто в танке.
Заранее благодарен.
компилятор все выполняет построчно. научись читать его построчно будет легче учить.
так вот:
16: компилятор обращается к строке 3 с параметром а=6. так как в мейн видит только свою переменную а. в функции doMath создается своя локальная переменная а значение которой взято из мейн.
7: переменной а присваивается значение из 6 на 4. переменной а из мейн в этом блоке не существует.
может тебя запутало что они одинаково называются.
могло быть вот так
функция doMath ничего не возвращает. она просто выводит на экран и сразу закрывается(ничего не нужно хранить в памяти чисто инструкции). а переменной a и b в функции doMath выделяется место в памяти и потом сразу удаляется
Спасибо за развернутый ответ. Как только отправил вопрос сразу понял что "функция doMath ничего не возвращает. она просто выводит на экран и сразу закрывается"
не понял как формируются две срединных строки. Почему сначала аргументу "a" сначала присваивается значение из main-на, а потом из doMath ? А "b" всегда остается из doMath ?
В ответе написан ход выполнения программы. Перечитайте его
Можете объяснить, что означает выход из области видимости(какой нибудь жизненный пример привести), и как переменные после этого уничтожаются.
Я хоть и внимательно прочитал и законспектировал, но эти 2 момента не понял
Андрей, ну вот представь подсобные хозяйства на селе: фигурные скобки в функции это ограничители вольера (область видимости), животные это переменные, килограммы массы — их значения. Какие имена хрюшек? Правильно! Внутри одной области видимости уникальные, а вне — одинаковые. Пусть дело происходит в мультфильме. Тогда мы можем взять килограммы одной хрюшки и не отобрать их у неё, а именно скопировать в другую хрюшку при этом вытеснив уже имевшиеся кг в последней! Андэсденд ми?
Спасибо за ответ.
Про область видимости я давно понял, а про уничтожение переменных не до конца понимал.
Я тут у прогеров поспрашивал и они сказали, что уничтожение — вынос мусора из памяти. И тогда да меня дошло
судя из теста понятно так же что уничтожаются аргументы ,сами то параметры остаются. Но аргумент так же уничтожается после объявления нового аргумента к этому же параметру . Как пример ,в функции майн() переопределил значение переменной B ( В=8),получается что предыдущее значение В (7) уничтожено. Так ли я понял урок?
По-сути в памяти мы берем ячейку и помещаем туда значение инициализированной переменной, а потом уже кладем в эту ячейку, то бишь изменяем значение переменной, различные значения. Получается, что переменная b создается один раз и уничтожается один раз, когда выходим из области видимости main.
Отличный урок! Да и все остальные тоже =) Понятным и доступным языком все объясняется.
Но вот проблема с этим уроком: не посмотреть ответ на тест =(
Починил.
Привет! Будешь дальше переводить оригинальный сайт?
Интересно почитать про жесть из STL.
Отличный сайтец, спасибо!
Привет. Да, малость уроков осталась. Самому тоже интересно дойти до STL.
Пожалуйста 🙂
Очень полезный урок. Спасибо!
Пожалуйста 🙂