Урок №49. Глобальные переменные

  Юрий  | 

    | 

  Обновл. 16 Апр 2019  | 

 21060

 ǀ   3 

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

Глобальными называются переменные, которые объявлены вне блока. Они имеют статическую продолжительность, т.е. создаются при запуске программы и уничтожаются при её завершении. Глобальные переменные имеют глобальную область видимости (или ещё «файловую область видимости»), то есть их можно использовать в любом месте файла, в котором они объявлены.

Определение глобальных переменных

Обычно глобальные переменные объявляют в верхней части кода, ниже директив #include, но выше любого другого кода. Например:

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

Результат выполнения программы выше:

Global value: 3
Local value: 9

Использовать одинаковые имена для локальных и глобальных переменных — это прямой путь к проблемам и ошибкам, поэтому подобное делать не рекомендуется. Многие разработчики добавляют к глобальным переменным префикс g_ (“g” от англ. “global”). Таким образом можно убить сразу двух зайцев: определить глобальные переменные и избежать конфликтов имён с локальными переменными.

Ключевые слова static и extern


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

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

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

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

Если вы хотите сделать глобальную переменную внутренней (которую можно использовать только внутри одного файла) — используйте ключевое слово static:

Аналогично, если вы хотите сделать глобальную переменную внешней (которую можно использовать в любом файле программы) — используйте ключевое слово extern:

По умолчанию, неконстантные переменные, объявленные вне блока, считаются внешними. Однако константные переменные, объявленные вне блока, считаются внутренними.

Предварительные объявления переменных с использованием extern

Из урока №20 мы уже знаем, что для использования функций, которые объявлены в другом файле, нужно использовать предварительные объявления.

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

global.cpp:

main.cpp:

Если предварительное объявление находится вне блока, то оно применяется ко всему файлу. Если же внутри блока, то оно применяется только к нему.

Если переменная объявлена с помощью ключевого слова static, то получить доступ к ней с помощью предварительного объявления не получится. Например:

constants.cpp:

main.cpp:

Обратите внимание, если вы хотите определить неинициализированную неконстантную глобальную переменную, то не используйте ключевое слово extern, иначе C++ будет думать, что вы пытаетесь записать предварительное объявление.

Связи функций


Функции имеют такие же свойства связи, что и переменные. По умолчанию они имеют внешнюю связь, которую можно сменить на внутреннюю с помощью ключевого слова static:

Предварительные объявления функций не нуждаются в ключевом слове extern. Компилятор может определить сам (по телу функции): определяете ли вы функцию или пишите её прототип.

Файловая область видимости vs. Глобальная область видимости

Термины «файловая область видимости» и «глобальная область видимости», как правило, вызывают недоумение, и это отчасти объясняется их неофициальным использованием. В теории, в C++ все глобальные переменные имеют файловую область видимости. Однако, по факту, термин “файловая область видимости” чаще применяется к внутренним глобальным переменным, а “глобальная область видимости” к внешним глобальным переменным.

Например, рассмотрим следующую программу:

global.cpp:

main.cpp:

g_y имеет файловую область видимости внутри global.cpp. Доступа к этой переменной вне файла global.cpp нет. Обратите внимание, хоть эта переменная и используется в main.cpp, сам main.cpp не видит её, он видит только предварительное объявление g_y (которое также имеет файловую область видимости). Линкер отвечает за связывание определения g_y в global.cpp с использованием g_y в main.cpp.

Глобальные символьные константы


В уроке о символьных константах, мы определяли их следующим образом:

constants.h:

Хоть это просто и отлично подходит для небольших программ, но каждый раз, когда constants.h подключается в другой файл, каждая из этих переменных копируется в этот файл. Таким образом, если constants.h подключить в 20 различных файлов, то каждая из переменных продублируется 20 раз. Header guards не остановят это, так как они только предотвращают подключение заголовочного файла более одного раза в один файл. Дублирование переменных на самом деле не является проблемой (поскольку константы зачастую не занимают много памяти), но изменение значения одной константы потребует перекомпиляции каждого файла, в котором она используется, что может привести к большим временным затратам в более крупных проектах.

Избежать этой проблемы можно, превратив эти константы в константные глобальные переменные, и изменив заголовочный файл только для хранения предварительных объявлений переменных. Например:

constants.cpp:

constants.h:

Их использование в коде остаётся неизменным:

Теперь определение символьных констант выполняется только один раз (в constants.cpp). Любые изменения, сделанные в constants.cpp, потребуют перекомпиляции только (одного) этого файла.

Но есть и обратная сторона медали: такие константы больше не будут считаться константами типа compile-time и, поэтому, не смогут использоваться где-либо, где потребуется константа такого типа.

Поскольку глобальные символьные константы должны находиться в отдельном пространстве имён и быть доступными только для чтения, то использовать префикс g_ уже не обязательно.

Предостережение о (неконстантных) глобальных переменных

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

Заключение

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

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

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

Используйте префикс g_ для идентификации ваших неконстантных глобальных переменных.

Вот примеры использования ключевых слов extern и static с неконстантными и константными переменными:

Тест

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

Ответ

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

Глобальные переменные имеют глобальную область видимости (или ещё «файловую область видимости»), что означает, что они доступны из точки объявления до конца файла, в котором объявлены.

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

Глобальные переменные могут иметь либо внутреннюю, либо внешнюю связь (что можно изменить через использование ключевых слов static и extern, соответственно).

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (92 оценок, среднее: 4,93 из 5)
Загрузка...

Комментариев: 3

  1. Аватар Алекс:

    Здарова!
    Уже не первый раз обращаюсь именно к этому уроку, с целью перечитать про глобальные переменные.
    Мне кажется было-бы круто, в название урока добавить например extern, через запятую после основного названия.
    Потому, что ищешь поиском по странице с содержанием и не находишь. Думаю я не первый и не последний такой :))
    А так все круто!

    PS: О! Ща куплю твою книгу!

    1. Юрий Юрий:

      Привет, чуть позже добавлю оглавление.

  2. Аватар Илья:

    Здравствуйте. Возник вопрос, в заголовке "Файловая область видимости vs глобальная область видимости" говорится, что такая переменная не будет использоваться во всех файлах. Почему?Она получила свое определение в одном файле, не константная, следовательно, имеет видимость во всех файлах. А в файле main.cpp она предварительно объявляется с помощью extern?

    Спасибо большое!

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

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