Области видимости

В этой статье мы рассмотрим области видимости данных(переменных, массивов и прочего). В языках программирования, обладающих мощным синтаксисом, обязательно есть в наличии так называемые блоки  Scope (ограничивающие области видимости данных). Конечно же в разных языках для этих блоков свое имя, например в языке СИ это скобочки {} , главное принцип работы!  Язык FreeBasic не стесняется впитывать все самое лучшее у популярных и мощных языков.
Блок Scope выглядит так:

Scope

End Scope

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

Dim a As Integer=55
Scope 
  Dim a As String="Hello"
  ? a
End Scope
? a
Sleep


Запустите этот пример. У вас должно высветиться два результата 55 и ниже Hello
 А теперь уберите блок Scope и попробуйте запустить. Как видите, компилятор не даст вам проделать такую операцию, написав примерно следующее: переменная А уже объявлена. Да действительно, одноименные переменные в одной области видимости можно объявить только один раз. И не важно что это за область видимости:

Основной код
Структура
Процедура
Функция
Классовые команды
Циклы
Логические команды If-endif , select case
и прочие блоки...

 Каждая из этих областей имеет свой собственный встроенный Scope. Но ничто вам не мешает в любой из областей видимости, размещать дополнительно, в неограниченных кол-вах свои области, как раз с помощью блока Scope. Еще один пример вложенных Scope:

Scope
    Dim a As String="Hello"
    ? a
    Scope
        Dim a As Integer=66
        ? a
    End Scope
End Scope
Sleep


Как видите блоки Scope можно вкладывать друг в друга, при том без ограничений. Блок подчищает за собой после своей работы, бережливо экономя память, и конечно уничтожается сам как объект. Как будто и не было ни переменных в нем, ни его самого. А теперь еще один пример, только уже  со знакомыми вам логическими операторами:

Dim a As Integer= 99
For a As Integer=1 To 2
    ? a
    If 5*3 =15 Then
        Dim a As Integer=33
        ? a
    Endif
Next
? a
Sleep


На этом примере четко видно. Мы объявили переменную один раз в основном блоке, другой раз в цикле и как бы два раза в блоке If  - endif . Но на самом деле все эти переменные разные. А что касается двух раз в блоке If  - endif , то я уже писал выше: как только блок завершает свою работу, он уничтожается. То есть проходя по циклу, каждый раз для блока If  - endif  выделяется свежее место в памяти. Поэтому переменная легко объявляется дважды. 

Но есть одна хитрость для всех Scope, кроме функций, процедур и команд классов. Если в самом Scope не объявлять свои переменные, то им доступны переменные из другой области видимости:

Dim a As Integer= 99
For a=1 To 2
    ? a
    If 5*3 =15 Then
        ? a
    Endif
Next
? a
Sleep


Обратили внимание на результат? А между тем нет ничего хитрого. Поскольку в областях видимости циклов и логических операторов, нет своих переменных, они пользуются общей. Мне например, очень нравится эта гибкость , которая была недоступна, когда я писал на другом языке программирования. А в итоге в проектах  с общим кол-вом строк более 1000, мне приходилось писать длинные имена переменных, либо обозначать их с цифрами, следить за именами переменных в циклах. Это крайне неудобно! А при написании программ на FreeBasic, начиная цикл, объявляю новую переменную для отсчета, и не боюсь, что она потом где-то в коде создаст баг(ошибку).

Как я и сказал с функциями и процедурами такой вариант не катит. Там "бронежелетный"  Scope. Пробить такой scope может лишь переменная объявленная как глобальная, то есть видимая везде. Но опять же, если вы в процедуре, в функции или в Scope, объявите свою переменную с таким же именем, то глобальная переменная ничего значить для этих блоков не будет. "Типа у нас своя и чужие нам без надобности."
Глобальная переменная объявляется как и простая, только добавляется дополнительно слово Shared:

Dim Shared a As Integer= 99
Sub my()
    ? a
    a+=1
End Sub
my()
? a
Sleep


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

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

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

Dim a As Integer= 99
Sub my()
    ? a
    a+=1
End Sub
my()
? a
Sleep


Компилятор откажется компилировать, поскольку процедуре неизвестна переменная a , точнее она не объявлена.

Dim Shared a As Integer= 99
Sub my()
    Dim a As Integer=4
    ? a
    a+=1
End Sub
my()
? a
Sleep


В таком примере глобальная переменная не видна в процедуре, поскольку у нее есть своя локальная

Dim Shared a As Integer= 99
Sub my(a As Integer)
    ? a
End Sub
my(77)
? a
Sleep


В таком примере, как и в прошлом, глобальная переменная не видна в процедуре, поскольку у нее есть своя локальная

И еще один пример для Scope:

Dim Shared a As Integer= 99
Scope
    Dim a As String="Freebasic"
    ? a
End Scope
? a
Sleep


Напишите свой пример с использованием select - case - end select и глобальной переменной.

Для функций , процедур и блоков Scope, очень часто бывает нужным, чтобы переменная или массив находящиеся в них не уничтожились(не очищались после работы этих блоков). То есть вызвали процедуру, сделали какие-то расчеты, а результат находится в процедуре, до следующих вызовов.  Для этого существует еще один тип объявленной переменной, имеющей название статическая или Static:

Sub my()
    Static a As Byte
    a+=1
    ? a
End Sub
my()
my()
my()
Sleep


Мы вызвали процедуру три раза и поскольку статическая переменная в процедуре сохраняется, то результаты за счет вычислений разные. Попробуйте вместо объявления с помощью Static, использовать в процедуре обычное объявление с помощью Dim.

А теперь пример с блоком Scope:

Sub my()
    Scope
        Static a As Byte=66
        a+=1
        ? a
    End Scope
End Sub
my()
my()
my()
Sleep


Здесь мы определили статической переменной первоначальное значение 66. При первом обращении переменной будет присвоено это значение.

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

Глобальный массив:

Dim Shared As String Array(10)

Статический массив:

Static As String Array(10)

Глобальный массив со своим типом данных:

Type my
    a As Byte=66
    b As Byte=55
End Type

Dim Shared As my Array(10)

Статическая переменная со своим типом данных:

Type my
    a As Byte=66
    b As Byte=55
End Type
Sub Smy()
    Static As my ST
End Sub

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

содержание | назад | вперед