Мы уже ранее рассматривали, что такое область видимости, продолжительность жизни, связи и то, какими они могут быть в языке С++. Давайте сейчас закрепим это всё.
Область видимости
Область видимости идентификатора определяет, где он доступен для использования. К идентификатору, который находится вне области видимости, доступ закрыт.
Переменные с локальной/блочной областью видимости доступны только в пределах блока, в котором они объявлены. Это:
локальные переменные;
параметры функции.
Переменные с глобальной/файловой областью видимости доступны в любом месте программы/файла. Это:
глобальные переменные.
Продолжительность жизни
Продолжительность жизни переменной определяет, где она создается и где уничтожается.
Переменные с автоматической продолжительностью жизни создаются в точке определения и уничтожаются при выходе из блока, в котором определены. Это:
обычные локальные переменные.
Переменные со статической продолжительностью жизни создаются, когда программа запускается, и уничтожаются при её завершении. Это:
глобальные переменные;
статические локальные переменные.
Переменные с динамической продолжительностью жизни создаются и уничтожаются по запросу программиста. Это:
динамические переменные (о них мы поговорим на соответствующем уроке).
Связи
Связь идентификатора определяет, относятся ли несколько упоминаний одного идентификатора к одному и тому же идентификатору или нет.
Идентификаторы без связей — это идентификаторы, которые ссылаются сами на себя. Это:
обычные локальные переменные;
пользовательские типы данных, такие как enum, typedef и классы, объявленные внутри блока (об этом детально поговорим на соответствующих уроках).
Идентификаторы с внутренней связью доступны в любом месте файла, в котором они объявлены. Это:
статические глобальные переменные (инициализированные или неинициализированные);
константные глобальные переменные;
статические функции (о них поговорим чуть позже).
Идентификаторы с внешней связью доступны как в любом месте файла, в котором они объявлены, так и в других файлах (через предварительное объявление). Это:
обычные функции;
неконстантные глобальные переменные (инициализированные или неинициализированные);
внешние константные глобальные переменные;
определяемые пользователем типы данных, такие как enum, typedef и классы с глобальной областью видимости (о них мы поговорим чуть позже).
Идентификаторы с внешней связью могут вызвать ошибку дублирования определений, если определения скомпилированы в более чем одном файле .cpp.
Функции по умолчанию имеют внешнюю связь, что можно изменить с помощью ключевого слова static (на внутреннюю связь).
Внимательные читатели могут заметить, что глобальные типы данных имеют внешнюю связь, но их определения не вызывают ошибки линкера при использовании в нескольких файлах. Это связано с тем, что типы, шаблоны и внешние встроенные функции являются исключениями из правила, и это позволяет им быть определенными более чем в одном файле, при условии, что эти определения идентичны. В противном случае, они не были бы так полезны.
Резюмируем
Весь материал, изложенный выше:
Тип | Пример | Область видимости | Продолжительность жизни | Связь | Примечание |
Локальная переменная | int x; | Локальная область видимости | Автоматическая продолжительность жизни | Нет связей | |
Статическая локальная переменная | static int s_x; | Локальная область видимости | Статическая продолжительность жизни | Нет связей | |
Динамическая переменная | int *x = new int; | Локальная область видимости | Динамическая продолжительность жизни | Нет связей | |
Параметр функции | void foo(int x) | Локальная область видимости | Автоматическая продолжительность жизни | Нет связей | |
Внешняя неконстантная глобальная переменная | int g_x; | Глобальная область видимости | Статическая продолжительность жизни | Внешняя связь | Инициализированная или неинициализированная. |
Внутренняя неконстантная глобальная переменная | static int g_x; | Глобальная область видимости | Статическая продолжительность жизни | Внутренняя связь | Инициализированная или неинициализированная. |
Внутренняя константная глобальная переменная | const int g_x(1); | Глобальная область видимости | Статическая продолжительность жизни | Внутренняя связь | Должна быть инициализирована. |
Внешняя константная глобальная переменная | extern const int g_x(1); | Глобальная область видимости | Статическая продолжительность жизни | Внешняя связь | Должна быть инициализирована. |
Предварительные объявления
С помощью предварительного объявления мы можем получить доступ к функции или переменной из другого файла:
Тип | Пример | Примечание |
Предварительное объявление функции | void foo(int x); | Только прототип, без тела функции. |
Предварительное объявление неконстантной глобальной переменной | extern int g_x; | Переменная НЕ должна быть инициализирована. |
Предварительное объявление константной глобальной переменной | extern const int g_x; | Переменная НЕ должна быть инициализирована. |
Почему-то возникает ошибка при компиляции файлов «undefined reference to `g_x1′». Можете подсказать, что я не так сделал? Как я понял, компилятор не связывает объявление «extern const int g_x1» и инициализацию в другом файле «const int g_x1 = 5». Но как это исправить?
При объявлении глобальной константы g_x1 на четвертой строке второго файла не было использовано ключевое слово extern. Константа доступна только файлу, в котором была объявлена
Спасибо большое за этот материал.
Пожалуйста)
Огромное СПАСИБОООО!!!!!
Пожалуйста 🙂
Внешняя неконстантная глобальная переменная может быть не инициализированной. Тогда вопрос, как компилятор понимает, что это определение переменной, а не предварительное объявление переменной определенной где-то во внешнем файле.
Например имеем код в начале файла:
Это определение не инициализированной неконстантной глобальной переменной, которую можно будет использовать где-то вне данного файла или это предварительное объявление (привязка на совести линкера)?
Понятно, что данный вопрос должен решаться на этапе компиляции, но как компилятор это поймет?
Ключевое слово extern, при ОПРЕДЕЛЕНИИ неконстантной глобальной переменной int g_y, в каком-нибудь внешнем файле, писать НЕ нужно. Как раз-таки когда мы хотим сделать ПРЕДВАРИТЕЛЬНОЕ ОБЪЯВЛЕНИЕ то пишем ключевое слово extern (мы как бы указываем компилятору с помощью слова extern что переменную g_y нужно использовать из другого файла)
Кажется это самый серьезный головняк из пройденного. Разнообразие, подобие и отличие всех этих вариантов переменных реальный треш. Вроде не сложно, но запомнить это — просто нечто…
Да, последние несколько тем — выносят мозг. Это все, конечно, нужно знать, но, имхо, только многократное применение на практике может помочь это все запомнить и корректно использовать… Я эти темы даже не стараюсь зазубрить и полностью понять, пока просто читаю для общего развития 🙂
Надо читать по настроению, не заучивать… Сложно, но понятно все.