Что значит extern c

extern

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

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

Файл 1 Файл 2
int х, у;
char ch;

void func1 (void)
<
x = 23;
>

extern int x, y;
extern char ch;

void func23(void)
<
y=10;
>

В файле 2 список глобальных переменных копируется из файла 1 и при объявлении добавляется спецификатор extern. Спецификатор extern сообщает компилятору, что следующие за ним типы и имена переменных объявляются где-то в другом месте. Другими словами, extern позволяет компилятору знать о типах и именах глобальных переменных без действительного создания этих переменных. Когда два модуля объединяются, все ссылки на внешние переменные пересматриваются.

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

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

int first, last; /*глобальное определение first и last */
int main (void)
<
extern int first; /* необязательное использование extern объявления */
.
>

Хотя объявление переменной с extern может иметь место в одном файле с объявлением глобальной переменной, в этом на самом деле нет необходимости. Если компилятор С встречает переменную, которая не была объявлена, то компилятор проверяет, соответствует ли она какой-либо глобальной переменной. Если это так, то компилятор предполагает, что эта переменная ссылается на глобальную.

Источник

Понимание ключевого слова extern в C

Я уверен, что этот пост будет столь же интересным и информативным для девственниц C (то есть для начинающих), так и для тех, кто хорошо разбирается в C. Итак, позвольте мне начать с того, что ключевое слово extern применяется к переменным C (объектам данных). и C. функции. В основном ключевое слово extern расширяет видимость переменных C и функций C. Вероятно, это причина, почему он был назван как extern.

Хотя (почти) каждый знает значение объявления и определения переменной / функции для полноты этого поста, я хотел бы прояснить их. Объявление переменной / функции просто объявляет, что переменная / функция существует где-то в программе, но память для них не выделена. Но объявление переменной / функции играет важную роль. И это тип переменной / функции. Поэтому, когда переменная объявлена, программа знает тип данных этой переменной. В случае объявления функции программа знает, каковы аргументы этой функции, их типы данных, порядок аргументов и тип возвращаемого значения функции. Так что это все о декларации. Что касается определения, когда мы определяем переменную / функцию, помимо роли объявления, она также выделяет память для этой переменной / функции. Поэтому мы можем думать об определении как о надмножестве объявления. (или объявление в качестве подмножества определения). Из этого объяснения должно быть очевидно, что переменная / функция может быть объявлена любое количество раз, но она может быть определена только один раз. (Помните основной принцип, что у вас не может быть двух местоположений одной и той же переменной / функции). Так что это все о декларации и определении.

Теперь вернемся к нашей главной цели: Понимание ключевого слова «extern» на языке C. Я объяснил роль объявления / определения, потому что необходимо понимать их, чтобы понимать ключевое слово «extern». Давайте сначала рассмотрим простой случай. Использование extern с функциями C. По умолчанию к объявлению и определению функции C добавлен «extern». Это означает, что хотя мы не используем extern с объявлением / определением функций C, он присутствует там. Например, когда мы пишем.

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

То же самое имеет место с определением функции C (определение функции C означает написание тела функции). Поэтому всякий раз, когда мы определяем функцию C, в начале определения функции там присутствует extern. Поскольку объявление может быть сделано любое количество раз, а определение может быть выполнено только один раз, мы можем заметить, что объявление функции может быть добавлено в несколько файлов C / H или в один файл C / H несколько раз. Но мы замечаем фактическое определение функции только один раз (т.е. только в одном файле). И так как extern расширяет видимость всей программы, функции можно использовать (вызывать) в любом месте любого файла всей программы, если известно объявление функции. (Зная объявление функции, компилятор C знает, что определение функции существует, и он собирается скомпилировать программу). Так что это все о extern с функциями C.

Теперь давайте возьмем второй и последний случай, т.е. использование extern с C-переменными. Я чувствую, что это более интересно и информативно, чем предыдущий случай, когда extern присутствует по умолчанию с функциями C. Итак, позвольте мне задать вопрос, как бы вы объявили переменную C без ее определения? Многие из вас сочли бы это тривиальным, но это важный вопрос для понимания extern с C-переменными. Ответ состоит в следующем.

Здесь была объявлена переменная целочисленного типа var (не помню определения, т.е. пока нет выделения памяти для var). И мы можем сделать это заявление столько раз, сколько необходимо. (помните, что объявление может быть сделано любое количество раз) Пока все хорошо. 🙂

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

Здесь переменная целочисленного типа с именем var была объявлена так же, как и определена. (помните, что определение является надмножеством объявления). Здесь также выделяется память для var. Теперь наступает сюрприз, когда мы объявили / определили функцию C, мы увидели, что extern присутствует по умолчанию. При определении функции мы можем без проблем добавить ее к extern. Но это не так с C переменными. Если мы поместим наличие extern в переменную по умолчанию, тогда память для них никогда не будет выделяться, они будут объявлены только. Поэтому мы явно указываем extern для переменных C, когда хотим объявить их, не определяя их. Кроме того, поскольку extern расширяет видимость всей программы, используя ключевое слово extern с переменной, мы можем использовать переменные в любом месте программы, если мы знаем их объявление и где-то определена переменная.

Теперь давайте попробуем понять extern с помощью примеров.

Источник

Что значит extern c

Вы наверняка знаете, что ключевое слово extern применяют для того, чтобы совместно использовать одну и ту же переменную в разных модулях кода на языке C/C++. С помощью extern переменные становятся глобальными. Но что в реальности происходит, когда используется extern? Что такое декларация? Какая область действия у переменной? Как правильно использовать extern?

Использование extern уместно только в тех случаях, когда построенная Вами программа состоит из нескольких исходных файлов, соединяемых вместе на этапе линковки, где некоторые переменные определены, например, в исходном файле file1.c, и к ним нужно обращаться в других исходных файлах, таких как file2.c.

Важно понимать разницу между терминами «определение переменной» (defining a variable) и «декларирование переменной» (иногда говорят «объявление переменной», declaring a variable). Причем можно определять и декларировать не только переменные, но и константы. Вот смысл этих понятий:

Переменная (или константа) определена в том месте программы, где компилятор выделяет под неё память. Переменная (или константа) декларируется, когда компилятор информируется о том, что эта переменная где-то уже определена.

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

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

Хотя есть и другие способы декларации и определения глобальных переменных, но самый надежный и удобный способ для этого — создать файл file3.h, в котором будет содержаться внешняя декларация переменной (с ключевым словом extern). Получится так называемый заголовочный файл, хедер (header). Этот хедер подключается один раз в том файле исходного кода, где переменная определена (хотя это не обязательно), и также хедер подключается во всех файлах исходного кода, где есть обращение к этой переменной. Для каждой программы один исходный файл (и только один исходный файл) декларирует переменную. Подобным образом только один хедер должен декларировать переменную. Ниже показан пример декларирования, определения и использования глобальной переменной global_variable.

file3.h

file1.c

file2.c

Подобным образом можно декларировать и определять функции. Пример (функции increment и use_it):

prog1.h

prog1.c

Модуль prog1 использует prog1.c, file1.c, file2.c, file3.h и prog1.h.

Примечание: по каким-то странным обстоятельствам, известным только Брайану Кернигану и Деннису Ритчи, при декларации функций использовать ключевое слово extern не обязательно. Это не относится к декларации переменных. Т. е., примеру вот эти две декларации функции совершенно равнозначны.

И так тоже правильно:

[Общие правила]

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

• Заголовочный файл (header, файл с расширением *.h) должен содержать в себе только extern-декларации переменных. Здесь не должно быть никаких статических переменных (static).

• Для любой имеющейся переменной только один заголовочный файл должен декларировать её (правило SPOT — Single Point of Truth, т. е. «правда только в одном месте»).

• Никогда не вставляйте в модуль исходного кода (файл с расширением *.c или *.cpp) extern-декларации переменных — исходные файлы всегда подключают (единственный) хедер, в котором декларируются переменные.

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

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

• В функции никогда не следует декларировать переменную с использованием extern.

• Избегайте использования глобальных переменных, где это только возможно — применяйте вместо этого функции.

Если Вы не являетесь опытным C-программистом, то можете (и возможно должны) дальше не читать.

[Не очень хороший способ определять глобальные переменные]

С некоторыми компиляторами языка C (в действительности, со многими) Вам может сойти с рук то, что можно назвать также «общим» определением переменной. Слово «общий» здесь относится к технике, используемой в языке Fortran для совместного использования переменной между исходными файлами, используя (возможно именованный) ОБЩИЙ блок кода. Подобное произойдет, когда каждый из нескольких файлов предоставят предварительное определение переменной. Пока не больше, чем в одном файле есть инициализированное определение, различные файлы используют одно общее определение переменной:

file10.c

file11.c

file12.c

Эта техника не придерживается букве стандарта C и правилу ‘одного определения’, однако стандарт C упоминает это как общую вариацию этого правила одного определения. Поскольку эта техника поддерживается не всегда, то лучше её избегать, особенно если Ваш код должен в будущем портироваться на другие платформы и системы. С использованием этой техники Вы можете также столкнуться с неожиданным каламбуром типов. Если в одном из файлов переменная i декларирована как double вместо int, то линковщики, не соблюдающие жесткую типизацию C, возможно не определят несоответствие типов. Если на Вашем компьютере у типов int и double разрядность 64 бита, то даже не получите предупреждения; но на компьютере, где int 32-битный, и double 64-битный, скорее всего линковщик выдаст предупреждение о разных размерах типов, и линковщик будет использовать самый большой размер, точно так же как программа на Фортране будет использовать самый большой размер переменной из любого общего блока.

Это упомянуто в C-стандарте Annex J как общее расширение:

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

В следующих 2 файлах приведен полный код для prog2:

prog2.h

prog2.c

Модуль программы prog2 использует prog2.c, file10.c, file11.c, file12.c, prog2.h.

Предупреждение: использование нескольких конкурентных определений для глобальной переменной приводит к неопределенному поведению, которое является способом для стандарта выразить, что «что-то, но непонятно что, должно произойти». Т. е. может произойти так, что в одном случае программа будет вести себя так, как ожидалось; как сказано в J.5.11, приблизительно «Вы могли бы быть удачливы чаще, чем того заслуживаете». Но программа, которая полагается не несколько определений внешней переменной — с ключевым словом ‘extern’ или без него — не только запутывает программиста, но и еще не гарантирует, что будет работать везде. Короче говоря: код будет содержать ошибку, которая может не показать себя.

[Нарушение правил]

faulty_header.h

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

broken_header.h

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

seldom_correct.h

Замечание 3: если в заголовке определена статическая переменная (с инициализацией значением или без), то каждый исходный файл, который подключит этот заголовок, получит свою собственную частную версию ‘глобальной’ переменной. В кавычках потому, что это уже не будет глобальная переменная для всех модулей программы, она будет глобальной только для одного модуля.

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

[Общие выводы]

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

Подобные проблемы возникают и с декларированием и определением функций — для этого работают аналогичные правила. К константам относится то же самое.

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

[Избегайте дублирования кода]

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

Другая проблема может возникнуть с тем, что переменные должны быть определены в каждой из нескольких «основных программ, main». Обычное побочное беспокойство; Вы можете просто представить исходный файл C для определения переменных и прилинковать его объектный файл к каждой из программ.

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

Источник

Читайте также:  Что значит произнести носом
Оцените статью