Пятница, 22.09.2017, 15:18
Приветствую Вас Гость | RSS

Кузница миров

Меню сайта
Категории раздела
Мои статьи [2]
Курс : "Основы С++ для начинающих программистов игр." [25]
WindMill Engine [3]
XNA4 RPG [0]
Перевод туториалов но созданию RPG на C# c XNA4.
C# & Leadwerks [5]
Программирование Leadwerks Engine на языке С# с помощью врапера Le.NET.

Каталог статей и уроков

Главная » Статьи » Курс : "Основы С++ для начинающих программистов игр."

5.2. Область видимости имен
5.2. Область видимости имен


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

int i;

void main()
{
   i=20;
}

void func()
{
   i = 321;
}


Это весь код, никаких инклюдов, ничего.

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

Code

int i;

i=123; // ошибка
void main()
{
   i=20;
}


то компилятор укажет на ошибку. Он решит, что вы пытаетесь заново объявить переменную i, да еще и неправильно, забыв указать тип. Если нужно задать глобальной переменной начальное значение, то делается это сразу при объявлении вот так:
Code

int i=123; // все ОК
void main()
{
   i=20;
}

Теперь слегка изменим пример:
Code

void func();

void main()
{
   int s;
   s = 10; // все ОК
}

void func()
{
   s = 321; // ошибка
}

Здесь внутри функции main объявлена переменная с, внутри самой функции с ней можно делать все что угодно. Но попытка использовать ее в функции func вызовет ошибку, поскольку func не принадлежит к области видимости функции main.
Казалось бы, чисто теоретически, можно было бы сделать вот так.
Code

void func();

void main()
{
   int s;
   s = 10; // все ОК
   void func()
   {
    s = 321;
   }
}

Но, не судьба. Запомните, объявлять и писать реализацию функции можно только в глобальной области. Поэтому попытка проделать такое вызовет ошибку.
Следующий пример:
Code

int s;

void main()
{
   s = 10; // все ОК
   while()
   {
    s = 321; // тоже все ОК
    int w;   
    w = 128; // все ОК
   }
   w = 512; // А вот здесь ошибка!
}

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

int s;

void main()
{
   s = 10; // все ОК
   {
    s = 321; // тоже все ОК
    int w;   
    w = 128; // все ОК
   }
   w = 512; // А вот здесь ошибка!
}

Здесь тот же пример но без цикла, тем не менее области видимости сохранились. Ключевое значение имеют только фигурные скобки. Но поскольку по синтаксису наличие таких скобок в цикле обязательно, то можно смело говорить что циклы имеют собственную локальную область видимости.

Ранее, описывая требования для имен переменных, я говорил, что имя должно быть уникальным. Теперь я хочу это правило дополнить. Имя должно быть уникальным в пределах одной области видимости.

То есть:
Code

void func();

void main()
{
   int s;
}

void func()
{
   int s;
}

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

int s;
   void func();

void main()
{
   int s;
   s = 10;
}

void func()
{
   s = 50;
}

Здесь переменная s объявлена также и в глобальной области. Казалось бы, она должна быть видимой и внутри других функций и подобная запись должна приводить к ошибке. Но нет!
Ранее озвученное правило продолжает действовать. Просто если имя объявлено в глобальной области, то везде используется оно, но если в локальной области объявлена переменная с аналогичным именем, то используется локальная.
Поэтому в приведенном выше примере

Присваивание s = 10; никак не затрагивает s глобальную. Тогда как в функции func присваивание производится как раз глобальной переменной. Так что можно даже сказать, что это две разные переменные. И следующая программа это продемонстрирует:
Code

#include <iostream>
using namespace std;

int s;
void func();

void main()
{
   setlocale(0,"");
   int s=30;
   cout<<"Локальная s ="<<s;
   func();
   cin.get();
}

void func()
{
   s = 10;
   cout<<"\nГлобальная s ="<<s;
}


Результат ее работы наглядно иллюстрирует, что изменение локальной переменной никак не влияет на ее глобального тезку.
Отлично. Все это хорошо. А вот что тогда делать, если нам зачем-то нужно обратится к этой самой глобальной одноименной переменной.
Для этого есть специальный оператор - ::. С его помощью можно запросто обратится к глобальной области. Как им пользоваться проиллюстрировано в примере ниже:
Code

#include <iostream>
using namespace std;

int s=10;

void main()
{
   setlocale(0,"");
   int s=30;
   cout << "Локальная s =" << s;
   cout << "\nГлобальная s =" << ::s; // Вот здесь вот оно, это двойное двоеточие
   cin.get();
}


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

void a()
{
   b();
}
...
void b()
{
   a();
}

Если объявления нет, то как же первая функция узнает о существовании второй?
Вот такой вариант не пройдет:
Code

void b()
{
   a();
}
...
void a()
{
   b();
}
...
void b()
{
   a();
}

Компилятор не допустит существования двух функций с одинаковыми именами. Да и не эффективно это, ведь функция может содержать несколько сотен строк кода. Поэтому, дабы избежать подобных недоразумений, и используется предварительное объявление:
Code

void b();

void a()
{
   b();
}
...
void b()
{
   a();
}

Проблемы области видимости.

А теперь поговорим о некоторых проблемах для локальных областей видимости. Есть у них один недостаток. Объявленная внутри локальной области переменная существует до тех пор, пока эта область активна. Рассмотрим следующий пример:

Code

int* func()
{
   int* pi;
   int i=55;
   pi = &i;
   return pi;
}

void main()
{
   int* pС;
   pС = func();

}


Сейчас нам необходимо вспомнить об указателях. Функции способны работать с ними так же, как и с любым другим типом данных. В приведенном примере func()
возвращает указатель на переменную i. Вот только сама переменная, по завершении работы функции, перестает существовать. В результате наш указатель указывает на все, что угодно, то есть по старому адресу, только не на существовавшую ранее переменную, а ее значение (55) окажется потерянным.
Хуже всего то, что компилятор никак не сможет помочь вам избежать таких вот ситуаций. Тут уж все в руках программиста.

На этом с видимостью завязываем, и вновь возвращаемся непосредственно к функциям. Ведь узнать нам придется еще очень много.
Категория: Курс : "Основы С++ для начинающих программистов игр." | Добавил: nilrem (11.05.2012)
Просмотров: 525 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0