Эффекты ключевого слова extern для функций C

В C я не заметил никакого эффекта ключевого слова 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

 #include  volatile unsigned int stop_now = 0; extern void bar_function(void); int main(void) { while (1) { bar_function(); stop_now = 1; } return 0; } 

Теперь, здесь bar.c

 #include  extern volatile unsigned int stop_now; void bar_function(void) { while (! stop_now) { printf("Hello, world!\n"); sleep(30); } } 

Как вы можете видеть, у нас нет общего заголовка между foo.c и bar.c, однако для bar.c требуется что-то, объявленное в foo.c, когда оно связано, и foo.c нуждается в функции из bar.c, когда она связана.

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

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

Технически каждая функция в публичном заголовке библиотеки является «extern», однако ее маркировка как таковая имеет очень мало пользы, в зависимости от компилятора. Большинство компиляторов могут понять это самостоятельно. Как вы видите, эти функции фактически определены где-то в другом месте.

В приведенном выше примере main () будет печатать мир hello только один раз, но продолжать вводить bar_function (). Также обратите внимание: bar_function () не будет возвращаться в этом примере (поскольку это просто простой пример). Представьте себе, что stop_now изменяется при обслуживании сигнала (следовательно, волатильности), если это не представляется практически достаточным.

Экстерны очень полезны для таких вещей, как обработчики сигналов, мьютексы, которые вы не хотите вводить в заголовок или структуру и т. Д. Большинство компиляторов оптимизируют, чтобы гарантировать, что они не резервируют память для внешних объектов, поскольку они знают, что они ‘Оставляем его в модуле, где объект определен. Однако, опять же, нет смысла указывать его с современными компиляторами при прототипировании публичных функций.

Надеюсь, это поможет 🙂

Насколько я помню стандарт, все объявления функций считаются «extern» по умолчанию, поэтому нет необходимости указывать его явно.

Это не делает это ключевое слово бесполезным, так как оно также может использоваться с переменными (и в этом случае – это единственное решение для решения проблем связи). Но с функциями – да, это необязательно.

Вам нужно различать две отдельные концепции: определение функции и объявление символа. «extern» – это модификатор привязки, подсказка для компилятора о том, где определен символ, обозначенный впоследствии (подсказка «не здесь»).

Если я напишу

 extern int i; 

в области файлов (вне функционального блока) в файле C, то вы говорите: «переменная может быть определена в другом месте».

 extern int f() {return 0;} 

является как объявлением функции f, так и определением функции f. Определение в этом случае превосходит extern.

 extern int f(); int f() {return 0;} 

это первое объявление, за которым следует определение.

Использование extern неверно, если вы хотите объявить и одновременно определить переменную области файла. Например,

 extern int i = 4; 

выдаст ошибку или предупреждение в зависимости от компилятора.

Использование extern полезно, если вы явно хотите избежать определения переменной.

Позволь мне объяснить:

Скажем, файл ac содержит:

 #include "ah" int i = 2; int f() { i++; return i;} 

Файл ah включает в себя:

 extern int i; int f(void); 

и файл bc содержит:

 #include  #include "ah" int main(void){ printf("%d\n", f()); return 0; } 

Внешний текст в заголовке полезен, поскольку он сообщает компилятору во время фазы ссылки: «это объявление, а не определение». Если я удалю строку в переменную ac, которая определяет i, выделяет для нее место и присваивает ему значение, программа не должна компилироваться с неопределенной ссылкой. Это говорит разработчику, что он ссылался на переменную, но еще не определил ее. Если, с другой стороны, я опускаю ключевое слово «extern» и int i = 2 строку int i = 2 , программа все еще компилирует – i будет определено со значением по умолчанию 0.

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

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

Ключевое слово extern принимает различные формы в зависимости от среды. Если объявление доступно, ключевое слово extern принимает ссылку, указанную ранее в блоке перевода. В отсутствие такой декларации extern указывает внешнюю связь.

 static int g(); extern int g(); /* g has internal linkage */ extern int j(); /* j has tentative external linkage */ extern int h(); static int h(); /* error */ 

Вот соответствующие абзацы из проекта C99 (n1256):

6.2.2 Связи идентификаторов

[…]

4 Для идентификатора, объявленного с помощью спецификатора classа хранения extern в области видимости, в которой видна предварительная декларация этого идентификатора, 23), если предыдущее объявление указывает внутреннюю или внешнюю связь, связь идентификатора с последующим объявлением одинакова как связь, указанная в предыдущем объявлении. Если ни одно предварительное объявление не отображается, или если в предыдущем объявлении не указана ссылка, то идентификатор имеет внешнюю привязку.

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

Встроенные функции имеют специальные правила о том, что означает extern . (Обратите внимание, что встроенные функции являются расширением C99 или GNU, а не оригиналом C.

Для не-встроенных функций extern не требуется, поскольку он включен по умолчанию.

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

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

объявление функции extern означает, что ее определение будет разрешено во время связывания, а не во время компиляции.

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

Я не думаю, что делать это было бы очень полезно, но такие эксперименты дают лучшее представление о том, как работает компилятор и компоновщик языка.

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

Надеюсь, что это ответ на ваш вопрос.

  • Соглашения о кодировании - перечисление имен
  • Пространства в URL-адресах?
  • Имеет ли printf ("% x", 1) неопределенное поведение?
  • Нет ли скобок в конструкторе без аргументов в качестве языка?
  • Каков правильный способ кодирования URL-символов Unicode?
  • Что может C / C ++ «потерять», если они определяют стандартный ABI?
  • Есть ли способ доступа к базовому контейнеру контейнеров-контейнеров STL?
  • Контейнеры стандартной библиотеки с дополнительными параметрами шаблона?
  • Каковы правила об использовании подчеркивания в идентификаторе C ++?
  • Использование Unicode в исходном коде на C ++
  • Может ли (доменное имя) поддомены подчеркивать «_»?
  • Давайте будем гением компьютера.