В Си я не заметил никакого эффекта от extern
ключевого слова, использованного до объявления функции. Сначала я подумал , что при определении extern int f();
в одном файле сил вы реализовать его за пределы объема файла. Однако я узнал, что оба:
extern int f();
int f() {return 0;}
и
extern int f() {return 0;}
компилируется просто отлично, без предупреждений от gcc. Я использовал gcc -Wall -ansi
; он даже не принял бы //
комментарии.
Существуют ли какие-либо эффекты для использования extern
перед определениями функций ? Или это просто необязательное ключевое слово без побочных эффектов для функций.
В последнем случае я не понимаю, почему стандартные дизайнеры решили засорять грамматику лишними ключевыми словами.
EDIT: Чтобы уточнить, я знаю , что есть использование для extern
переменных, но я спрашиваю только о extern
в функциях .
Ответы:
У нас есть два файла, foo.c и bar.c.
Вот foo.c
Теперь вот bar.c
Как вы можете видеть, у нас нет общего заголовка между foo.c и bar.c, однако bar.c требуется что-то объявленное в foo.c, когда он связан, и foo.c нужна функция из bar.c, когда он связан.
Используя extern, вы сообщаете компилятору, что все, что следует за ним, будет найдено (нестатично) во время ссылки; не оставляйте ничего для этого в текущем проходе, так как это будет встречено позже. В этом отношении функции и переменные обрабатываются одинаково.
Это очень полезно, если вам нужно разделить какой-то глобал между модулями и не хотите помещать / инициализировать его в заголовке.
Технически, каждая функция в общедоступном заголовке библиотеки является «внешней», однако маркировка их как таковых имеет очень мало или вообще никаких преимуществ, в зависимости от компилятора. Большинство компиляторов могут понять это самостоятельно. Как видите, эти функции на самом деле определены где-то еще.
В приведенном выше примере main () напечатает hello world только один раз, но продолжит ввод bar_function (). Также обратите внимание, что bar_function () не собирается возвращаться в этом примере (так как это простой пример). Просто представьте, что stop_now модифицируется, когда сигнал обслуживается (следовательно, изменчив), если это не кажется достаточно практичным.
Экстерьеры очень полезны для таких вещей, как обработчики сигналов, мьютекс, который вы не хотите помещать в заголовок или структуру и т. Д. Большинство компиляторов оптимизируют работу, чтобы гарантировать, что они не резервируют память для внешних объектов, так как они знают, что они Я буду резервировать его в модуле, где определен объект. Однако, опять же, нет смысла указывать его современными компиляторами при создании прототипов открытых функций.
Надеюсь, это поможет :)
источник
bar.c
и объявлением вfoo.c
. Если функция объявлена вfoo.h
и оба файла включеныfoo.h
, то заголовок обеспечивает согласованность между двумя исходными файлами. Без этого, если определениеbar_function
inbar.c
изменяется, но объявление infoo.c
не изменяется, то во время выполнения все идет не так; компилятор не может определить проблему. С правильно используемым заголовком компилятор обнаруживает проблему.Насколько я помню стандарт, все объявления функций по умолчанию считаются «внешними», поэтому нет необходимости указывать его явно.
Это не делает это ключевое слово бесполезным, поскольку его также можно использовать с переменными (и в этом случае - это единственное решение для решения проблем с сцеплением). Но с функциями - да, это необязательно.
источник
Необходимо различать два отдельных понятия: определение функции и объявление символа. «extern» - это модификатор связи, подсказка компилятору о том, где определяется символ, на который ссылаются впоследствии (подсказка «не здесь»).
Если я напишу
в области видимости файла (вне функционального блока) в файле C вы говорите: «переменная может быть определена в другом месте».
является одновременно объявлением функции f и определением функции f. Определение в этом случае переопределяет внешний.
сначала декларация, затем определение.
Использование
extern
неправильно, если вы хотите объявить и одновременно определить переменную области файла. Например,выдаст ошибку или предупреждение, в зависимости от компилятора.
Использование
extern
полезно, если вы явно хотите избежать определения переменной.Позволь мне объяснить:
Допустим, файл ac содержит:
Файл ах включает в себя:
и файл bc содержит:
Экстерьер в заголовке полезен, потому что он сообщает компилятору на этапе компоновки: «это объявление, а не определение». Если я удалю строку в ac, которая определяет i, выделит для нее место и присвоит ей значение, программа не сможет скомпилироваться с неопределенной ссылкой. Это говорит разработчику, что он ссылался на переменную, но еще не определил ее. Если, с другой стороны, я опускаю ключевое слово "extern" и удаляю
int i = 2
строку, программа все равно компилируется - для меня будет задано значение по умолчанию 0.Переменные области файла неявно определяются со значением по умолчанию, равным 0 или NULL, если вы не присваиваете им значение явно, в отличие от переменных области блока, которые вы объявляете в верхней части функции. Ключевое слово extern избегает этого неявного определения и, следовательно, помогает избежать ошибок.
Для функций в объявлениях функций ключевое слово действительно избыточно. Объявления функций не имеют неявного определения.
источник
int i = 2
строку в -3 абзаце? И правильно ли утверждать, видяint i;
, компилятор будет выделять память для этой переменной, но, видяextern int i;
, компилятор НЕ будет выделять память, а будет искать переменную в другом месте?extern
Ключевое слово принимает различные формы в зависимости от окружающей среды. Если объявление доступно,extern
ключевое слово принимает связь, указанную ранее в модуле перевода. При отсутствии какой-либо такой декларации,extern
указывается внешняя связь.Вот соответствующие параграфы из проекта C99 (n1256):
источник
Встроенные функции имеют специальные правила о том, что
extern
означает. (Обратите внимание, что встроенные функции являются расширением C99 или GNU; их не было в оригинальном C.Для не встроенных функций
extern
не требуется, так как включено по умолчанию.Обратите внимание, что правила для C ++ разные. Например,
extern "C"
требуется объявление C ++ функций C, которые вы собираетесь вызывать из C ++, и существуют разные правилаinline
.источник
Вот почему 10 лет спустя:
extern
в объявлении функции для удаления;git/git
этому выводу, и удаляемаяextern
из ее кода (для Git 2.22, Q2 2019).Смотрите коммит ad6dad0 , коммит b199d71 , коммит 5545442 (29 апреля 2019 г.) от Denton Liu (
Denton-L
) .(Слиты Junio C Hamano -
gitster
- в фиксации 4aeeef3 , 13 мая 2019)Это не всегда просто, хотя:
См. Коммит 7027f50 (04 сентября 2019 г.) Дентона Лю (
Denton-L
) .(Слиты Denton Лю -
Denton-L
- в фиксации 7027f50 , 05 Sep 2019)Обратите внимание, что с Git 2.24 (Q4 2019), любой паразит
extern
отбрасывается.См. Коммит 65904b8 (30 сентября 2019 г.) Эмили Шаффер (
nasamuffin
) .Помогает: Джефф Кинг (
peff
) .См. Коммит 8464f94 (21 сентября 2019 г.) Дентона Лю (
Denton-L
) .Помогает: Джефф Кинг (
peff
) .(Слиты Junio C Hamano -
gitster
- в фиксации 59b19bc , 7 октября 2019)источник
В
extern
ключевое слово информирует компилятор о том , что функция или переменная имеет внешнее связывание - другими словами, это видно из других , чем тот , в котором он определен файлов. В этом смысле оно имеет противоположное значениеstatic
ключевому слову. Это немного странно, чтобы поместитьextern
во время определения, так как никакие другие файлы не будут иметь видимость определения (или это приведет к нескольким определениям). Обычно вы помещаетеextern
объявление в какой-то момент с внешней видимостью (например, файл заголовка) и помещаете определение в другое место.источник
объявление функции extern означает, что ее определение будет разрешено во время компоновки, а не во время компиляции.
В отличие от обычных функций, которые не объявлены как extern, они могут быть определены в любом из исходных файлов (но не в нескольких исходных файлах, в противном случае вы получите ошибку компоновщика, говорящую о том, что вы дали несколько определений функции), включая одно в который объявлен extern.So, в этом случае компоновщик разрешает определение функции в том же файле.
Я не думаю, что это было бы очень полезно, однако проведение подобных экспериментов дает лучшее представление о том, как работает компилятор и компоновщик языка.
источник
Причина этого не в том, что во время компоновки компоновщик пытается разрешить внешнее определение (в вашем случае
extern int f()
). Не имеет значения, находит ли он его в том же файле или в другом файле, если он найден.Надеюсь, что это ответ на ваш вопрос.
источник
extern
вообще добавлять к какой-либо функции?