Что такое прототип процедуры
Перейти к содержимому

Что такое прототип процедуры

  • автор:

Прототипы функций в С++

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

Итак, что же такое прототип функции? Прототип имеет следующий вид.

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

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

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

Как лучше всего использовать прототип функции. Лучше всего описание функции включить в отдельный исходный файл. После этого надо скомпилировать этот файл и получить объектный файл. Прототип следует помесить в заголовочный файл и включать его директивой #include в те исходные файлы, в которых присутствует вызов функции.

Некоторые сведения о Perl 5/Функции и процедуры

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

  • они позволяют разбить одну большую программу на несколько небольших частей, чтобы сделать ее понятной;
  • они объединяют операторы в единый фрагмент, который может повторно вызываться, возможно при разных входных параметрах.

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

Содержание

Определение процедуры [ править ]

Процедура может быть объявлена в любом месте основной программы следующим образом:

  • <имя-процедуры> — имя процедуры, по которому ее можно вызывать явно.
  • (<прототип>) — специальная строка, которая описывает какие парараметры разрешено передавать процедуре.
  • <<основной-блок>>— блок операторов, являющийся определением процедуры и выполняющийся при каждом ее вызове.

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

Тело процедуры может хранится в разных местах:

  • оно может быть определено в том же исходном файле;
  • оно может быть определено в отдельном файле и загружаться с помощью do , require и use ;
  • текст процедуры может быть передан функции eval() : в этом случае компиляция процедуры будет происходить каждый раз при вызове;
  • текст процедуры может быть определен анонимно и вызываться по ссылке.

Вызов процедуры [ править ]

Процедуру можно вызвать, указав ее имя с идентификатором типа & :

Если процедура вызывается со скобками после имени, причем не важно есть ли аргументы у функции на самом деле или нет, идентификатор типа & можно опускать:

Если перед вызовом процедура была определена или импортирована, то можно опустить и & и скобки:

Если процедура анонимная, то идентификатор типа & в вызове обязателен:

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

Если процедура является целым файлом, то ее можно вызвать как perl-подпрограмму с помощью конструкции do <имя-файла>; . Если указываемый файл недоступен для чтения, то do возвращает неопределенное значение, а встроенной переменной $! будет присвоен код ошибки. Если файл может быть прочитан, но возникают ошибки при компиляции или во время исполнения, то do возвращает неопределенное значение, а в специальной переменной $@ будет хранится сообщение об ошибке.

Функция eval() [ править ]

рассматривает параметр <выражение> как текст программы Perl, компилирует его и, если не обнаруживает ошибок, выполняет в текущем вычислительном окружении. Если выражение передано блоком, то оно анализируется и компилируется один раз. Это удобно, так как ошибки обнаруживаются раньше. Если аргумент передается не блоком, то его ошибки будут обнаружены только во время исполнения.

Если <выражение> отсутствует, то по умолчанию используется переменная $_ . Если выражение завершается успешно, то она возвращает последнее вычисленное значение внутри <выражение> .

Если <выражение> содержит синтаксические ошибки или вызывается die() , или при исполнении возникает ошибка, то eval() возвращает неопределенное значение, а в специальную переменную $@ заносится сообщение об ошибке.

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

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

Область видимости процедуры [ править ]

Точкой определения переменной в Perl является то место, где она впервые встречается в программе. Область действия большинства переменных ограничена пакетом. Исключение составляют некоторые специальные предопределенные переменные интерпретатора perl. Пакет — это способ порождения пространства имен для части программы. Другими словами, каждый фрагмент кода относится к какому-то пакету.

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

Перменные, которые видны только конкретной процедуре, называются локальными (говорят что они лексические) и такие переменные обладают локальной видимостью (lexical scope). В Perl существует два способа порождения локальных переменных: при помощи функции my() и local() .

Функция my() [ править ]

Функция my() используется для объявления одной или нескольких переменных локальными и ограничивает область их действия:

  • процедурой/функцией;
  • блоком;
  • выражением, переданным eval() ;
  • исходным файлом программы, в зависимости от того, в каком месте была вызвана my() .

Функция local() [ править ]

Функция local() вызывается аналогично my() , но создает не совсем локальные переменные, а временно заменяет текущие значения глобальных переменных локальными значениями внутри:

  • процедуры/функции;
  • блока;
  • выражения, переданного eval() ;
  • исходного файла с программой, в зависимости от того, в каком месте вызвана local() .

Если при вызове функции глобальная переменная существует, то ее предыдущее значение сохраняется в стеке и заменяется новым значением. После выхода переменной из области видимости процедуры/блока/функции eval() или файла, ее предыдущее значение восстанавливается из стека. Такие переменные иногда называют динамическими, а их область видимости — динамической областью видимости.

Функция my() появилась в Perl с пятой версии, позже local() , однако для создания истинно локальных переменных рекомендуется использовать именно функцию my() . Впрочем и у local() есть причины для применения.

Рекомендации по использованию my() и local() [ править ]

Функция my() должна использоваться всегда, кроме следующих случаев, когда нужно использовать local() :

  • Присваивание временного значения глобальной переменной, в первую очередь это относится к предопределенным глобальным переменным типа $_ , $ARGV и другие.
  • Создание локального дескриптора файла, каталога или локального псевдонима или функции.
  • Временное изменение массива или хеш-массива. Например, так следует поступать, если нужно временно изменить переменные окружения в предопределенном хеш-массиве %ENV .

Функция our() [ править ]

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

Использование этой функции в общем то не обязательно, так как любая переменная получает эту видимость по умолчанию. Данная функция используется в следующих ситуациях:

  • Когда включена директива use strict ‘vars’; , которая требует явного указания области видимости переменной в любом месте, где она действует.
  • Когда переменная используется в блоках, например

Функция our() наделяет переменную пакетной видимостью на протяжении всего лексического пространства. Сравните

Такое поведение отличает our() от до этого использовавшейся директивы use vars , которая позволяла использовать неквалифицированные имена только внутри текущего пакета. Помните, что с версии 5.6.0 использование use vars считается устаревшим подходом. Используйте только our() .

Передача аргументов в процедуру [ править ]

Данные в процедуру передаются через ее аргументы. Для передачи аргументов используется специальный массив @_ , в котором $_[0] – первый параметр, $_[1] – второй параметр и так далее. Такой механизм позволяет передать в процедуру произвольное число аргументов.

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

Чтобы реализовать передачу по значению, вы должны создать внутри процедуры локальные переменные и скопировать в них значения из @_ . Обычно это делается так

или с помощью функции shift , каждый вызов которой возвращает очередной элемент массива @_

Передача аргументов по ссылкам [ править ]

К сожалению массивы не могут быть просто так переданы в процедуру с сохранением их идентичности. Если параметр является массивом или хеш-массивом, все его элементы сохраняются в @_ . При передаче в подпрограмму нескольких массивов, все они будут перемешаны в одном @_ :

Передавать массивы можно одним из двух способов.

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

В этом примере в процедуру мы передаем не сами массивы, а переменные типа typeglob , которые легко выделить из @_ , так как фактически они являются скалярами. Мы использовали здесь local() вместо my() потому, что typeglob представляет запись в таблице символов и поэтому не может быть локальной. Запись local (*array, *hash) = @_; создает синонимы (псевдонимы), т.е. *array фактически создает псевдоним для *list , а *hash для *person . Таким образом, любые изменения по псевдонимам будут приводить к изменениям в оригиналах.

Второй подход, более новый, связан с передачей ссылок на массивы. Ссылка является скаляром, поэтому ее легко выделить в @_ . Таким образом, внутри процедуры достаточно просто применять операции разыменования ссылок. Вышеприведенный пример может быть переписан:

В данном случае мы скопировали ссылки в локальные переменные при помощи my() . Изменение оригинальных массивов через ссылки должна быть понятна.

Прототипы [ править ]

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

Чтобы контролировать вызов процедур на этапе компиляции, используются прототипы. Прототип процедуры/функции — это строка из списка символов, определяющая количество и типы передаваемых параметров. Например следуюшая функция

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

  • $ (скаляр)
  • @ (массив)
  • % (хеш-массив)
  • & (анонимная процедура)
  • * (typeglob)

Если поставить перед символом в прототипе обратный слеш, например sub example (\$) <. >, то имя фактического параметра всегда должно начинаться с идентификатора этого типа. В этом случае внутри процедуры в массиве параметров @_ будет передаваться ссылка на фактический параметр, указанный при вызове. Это позволяет упростить передачу массивов по ссылкам, например, сравните

Обязательные параметры отделяются от необязательных внутри прототипа символом ; . Например

имеет 4 параметра, первые 3 из которых обязательны.

Следует помнить, что прототип не проверяется, когда вызов процедуры начинается с амперсанда & ( &example ).

Контекст выполнения функции [ править ]

Каждая функция может узнать в каком контексте она выполняется и в зависимости от этого отдавать результат в нужном контексте. Для этого используется функция wantarray() , которая возвращает ИСТИНУ, если функция или блок eval<> был вызван в списковом контексте, иначе ЛОЖЬ. Функция возвращает undef значение, если она была вызвана в void-контексте.

Следующий небольшой пример демонстрирует работу этой функции.

Рекурсивные вызовы [ править ]

Язык Perl допускает, чтобы процедура/функция вызывала саму себя. Такие вызовы называются рекурсивными.

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

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

Прототипы в JS и малоизвестные факты

Получив в очередной раз кучу вопросов про прототипы на очередном собеседовании, я понял, что слегка подзабыл тонкости работы прототипов, и решил освежить знания. Я наткнулся на кучу статей, которые были написаны либо по наитию автора, как он «чувствует» прототипы, либо статья была про отдельную часть темы и не давала полной картины происходящего.

Оказалось, что есть много неочевидных вещей из старых времён ES5 и даже ES6, о которых я не слышал. А еще оказалось, что вывод консоли браузера может не соответствовать действительности.

Что такое прототип

Объект в JS имеет собственные и унаследованные свойства, например, в этом коде:

у объекта foo имеется собственное свойство bar со значением 1 , но также имеются и другие свойства, такие как toString . Чтобы понять, как объект foo получает новое свойство toString , посмотрим на то, из чего состоит объект:

Дело в том, что у объекта есть ссылка на другой объект-прототип. При доступе к полю foo.toString сначала выполняется поиск такого свойства у самого объекта, а потом у его прототипа, прототипа его прототипа, и так пока цепочка прототипов не закончится. Это похоже на односвязный список объектов, где поочередно проверяется объект и его объекты-прототипы. Так реализовано наследование свойств, например, у (почти, но об этом позже) любого объекта есть методы valueOf и toString .

Как выглядит прототип

У всех прототипов имеются два общих свойства, constructor и __proto__ . Свойство constructor указывает на функцию-конструктор, с помощью которой создавался объект, а свойство __proto__ указывает на следующий прототип в цепочке (либо null, если это последний прототип). Остальные свойства доступны через . , как в примере выше.

Да кто такой этот ваш constructor

constructor – это ссылка на функцию, с помощью которой был создан объект:

Не совсем понятна идея зачем он был нужен, возможно, как способ клонирования объекта:

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

Где живёт прототип

На самом деле, объекты представляют собой не только поля, доступные для JS кода. Интерпретатор также сохраняет некоторые приватные данные объекта для работы с ним, для этого в стандарте определено понятие внутренних слотов, которые обозначены как имя в квадратных скобках [[SlotName]] . Для прототипов отведен приватный слот [[Prototype]] содержащий ссылку на объект-прототип (либо null , если прототипа нет).

Из-за того, что [[Prototype]] предназначался исключительно для самого JS движка, получить доступ к прототипу объекта было невозможно. Для случаев когда это было нужно, ввели нестандартное свойство __proto__ , которое поддержали многие браузеры и которое по итогу попало в сам стандарт, но как опциональное и стандартизированное только для обратной совместимости с существующим JS кодом.

О чем вам недоговаривает дебаггер, или он вам не прототип

Свойство __proto__ является геттером и сеттером для внутреннего слота [[Prototype]] и находится в Object.prototype :

Из-за этого я избегал записи __proto__ для обозначения прототипа. __proto__ находится не в самом объекте, что приводит к неожиданным результатам. Для демонстрации попробуем через __proto__ удалить прототип объекта и затем восстановить его:

Как так получилось? Дело в том, что __proto__ – это унаследованное свойство Object.prototype , а не самого объекта foo . Из-за этого в момент когда в цепочке прототипов пропадает ссылка на Object.prototype , __proto__ превращается в тыкву и перестает работать с прототипом.
А теперь отработаем кликбейт из введения. Представим следующую цепочку прототипов:

В консоли Chrome foo будет выглядеть следующим образом:

А теперь уберем связь между baz и Object.prototype :

И теперь в консоли Chrome видим следующий результат:

Связь с Object.prototype разорвана у baz и __proto__ возвращает undefined даже у дочернего объекта foo , однако Chrome все равно показывает что __proto__ есть. Скорее всего тут имеется в виду внутренний слот [[Prototype]] , но для простоты это было изменено на __proto__ , ведь если не извращаться с цепочкой прототипов, это будет верно.

Как работать с прототипом объекта

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

Для изменения прототипа у существующего объекта есть всего два метода: использование сеттера __proto__ и метод Object.setPrototypeOf .

Если браузер не поддерживает ни один из этих методов, то изменить прототип объекта невозможно, можно только создать его копию с новым прототипом.
Но есть один нюанс с внутренним слотом [[Extensible]] который указывает на то, возможно ли добавлять к нему новые поля и менять его прототип. Есть несколько функций, которые выставляют этот флаг в false и предотвращают смену прототипа: Object.freeze , Object.seal , Object.preventExtensions . Пример:

А теперь менее категоричный вопрос создания нового объекта с прототипом. Для этого есть следующие способы.
Стандартный способ:

Если нет поддержки Object.create , но есть __proto__ :

И в случае если отсутствует поддержка всего вышеперечисленного:

Способ основан на логике работы оператора new , о которой поговорим чуть ниже. Но сам способ основан на том, что оператор new берет свойство prototype функции и использует его в качестве прототипа, т.е. устанавливает объект в [[Prototype]] , что нам и нужно.

Функции и конструкторы

А теперь поговорим про функции и как они работают в качестве конструкторов.

Функция Person тут является конструктором и создает два поля в новом объекте, а цепочка прототипов выглядит так:

Откуда взялся Person.prototype ? При объявлении функции, у нее автоматически создается свойство prototype для того чтобы ее можно было использовать как конструктор (note 3), таким образом свойство prototype функции не имеет отношения к прототипу самой функции, а задает прототипы для дочерних объектов. Это позволит реализовывать наследование и добавлять новые методы, например так:

И теперь вызов user.fullName() вернет строку «John Doe».

Что такое new

На самом деле оператор new не таит в себе никакой магии. При вызове new выполняет несколько действий:

  1. Создает новый объект self
  2. Записывает свойство prototype функции конструктора в прототип объекта self
  3. Вызывает функцию конструктор с объектом self в качестве аргумента this
  4. Возвращает self если конструктор вернул примитивное значение, иначе возвращает значение из конструктора

Все эти действия можно сделать силами самого языка, поэтому можно написать свой собственный оператор new в виде функции:

Но начиная с ES6 волшебство пришло и к new в виде свойства new.target, которое позволяет определить, была ли вызвана функция как конструктор с new, или как обычная функция:

new.target будет undefined для обычного вызова функции, и ссылкой на саму функцию в случае вызова через new ;

Наследование

Зная все вышеперечисленное, можно сделать классическое наследование дочернего класса Student от класса Person . Для этого нужно

  1. Создать конструктор Student с вызовом логики конструктора Person
  2. Задать объекту `Student.prototype` прототип от `Person`
  3. Добавить новые методы к `Student.prototype`

Фиолетовым цветом обозначены поля объекта (они все находятся в самом объекте, т.к. this у всей цепочки прототипов один), а методы желтым (находятся в прототипах соответствующих функций)
Вариант 1 предпочтительнее, т.к. Object.setPrototypeOf может привести к проблемам с производительностью.

Сколько вам сахара к классу

Для того чтобы облегчить классическую схему наследование и предоставить более привычный синтаксис, были представлены классы, просто сравним код с примерами Person и Student:

Уменьшился не только бойлерплейт, но и поддерживаемость:

  • В отличие от функции конструктора, при вызове конструктора без new выпадет ошибка
  • Родительский класс указывается ровно один раз при объявлении

При этом цепочка прототипов получается идентичной примеру с явным указанием prototype у функций конструкторов.

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

P. P. S.

К сожалению главный кликбейт статьи перестал быть актуальным. В данный момент Chrome (версия 93, на момент обновления статьи) перестал использовать __proto__ для обозначения прототипа, и теперь отображает его как слот [[Prototype]] :

image

Справедливости ради хочу отметить что в Firefox (92) также не используется обозначение __proto__ :

13.3. Прототип функции.

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

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

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

Прототип функции имеет вид:

тип_результата имя_функции (список) ;

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

Пример описания функции fun, которая имеет три параметра типа int, один параметр типа double и возвращает результат типа double:

double fun(int, int, int, double);

Пример описания для вышеприведенной функции Min:

int Min (int x, int y);

int Min (int, int);

13.4. Область видимости.

Область видимости (действия) объекта (переменной и др.) – это та часть кода (текста) программы, в которой его можно использовать.

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

Основное правило видимости в языке Си: объект, объявленный внутри блока (участка программы, заключенного в фигурные скобки), как правило, виден, начиная с места его объявления и заканчивая концом этого блока ( > ). Если объявление данных лежит внутри нескольких входящих друг в друга блоков, оно считается расположенным в самом внутреннем из них. Если же нужно сделать объект видимым за пределами блока, нужно объявить его вне блока. Можно, например, объявить переменные вне всех функций; такие переменные называются глобальными, в отличие от переменных, объявленных внутри функции, называемых локальными и видимых только в пределах своей функции.

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

Областью действия глобальных данных считается файл, в котором они определены, от точки описания до его окончания.

Если некоторое место программы входит в область видимости двух и более переменных с одинаковыми именами, объявленных на разном уровне вложенности блоков, действует та из них, которая объявлена на самом глубоком уровне. Объявление в одном и том же блоке на одном и том же уровне (или глобально — в одном и том же файле) переменных с одинаковыми именами вызывает ошибку компиляции.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *