Мой вопрос в том, когда ссылка на функцию должна быть указана с ключевым словом extern
в C.
Я не вижу, когда это следует использовать на практике. Когда я пишу программу, все функции, которые я использую, становятся доступными через файлы заголовков, которые я включил. Итак, почему было бы полезно extern
получить доступ к тому, что не было показано в файле заголовка?
Я мог подумать о том, как extern
работает некорректно, и если да, пожалуйста, исправьте меня.
Изменить: Если вы extern
что-то, когда оно является объявлением по умолчанию без ключевого слова в файле заголовка?
"extern
" изменяет привязку. С ключевым словом предполагается, что функция /variable доступна где-то в другом месте, и разрешение отложено на компоновщик.
Существует разница между "extern" на функциях и на переменных: по переменным он не создает экземпляр самой переменной, то есть не выделяет никакой памяти. Это нужно сделать где-то еще. Поэтому важно, если вы хотите импортировать переменную из другого места. Для функций это говорит только компилятору, что ссылка является extern. Поскольку это значение по умолчанию (вы используете ключевое слово "static", чтобы указать, что функция не связана с использованием extern linkage), вам не нужно явно ее использовать.
extern сообщает компилятору, что эти данные определены где-то и будут связаны с компоновщиком.
С помощью ответов здесь и беседы с несколькими друзьями здесь приведен практический пример использования extern.
Пример 1 -, чтобы показать ловушку:
File stdio.h:
int errno;
/* other stuff...*/
myCFile1.c:
#include <stdio.h>
Code...
myCFile2.c:
#include <stdio.h>
Code...
Если myCFile1.o и myCFile2.o связаны, каждый из файлов c имеет отдельные копии errno. Это проблема, поскольку предполагается, что один и тот же errno доступен во всех связанных файлах.
Пример 2 - Исправление.
File stdio.h:
extern int errno;
/* other stuff...*/
File stdio.c
int errno;
myCFile1.c:
#include <stdio.h>
Code...
myCFile2.c:
#include <stdio.h>
Code...
Теперь, если оба myCFile1.o и MyCFile2.o связаны линкером, они оба указывают на тот же errno. Таким образом, решая реализацию с помощью extern.
Уже указано, что ключевое слово extern
избыточно для функций.
Что касается переменных, разделяемых между единицами компиляции, вы должны объявить их в файле заголовка с ключевым словом extern, а затем определить их в одном исходном файле без ключевого слова extern. Единственный исходный файл должен быть разделяющим имя файла заголовка для лучшей практики.
В C термин "extern" подразумевается для прототипов функций, поскольку прототип объявляет функцию, которая определена где-то в другом месте. Другими словами, прототип функции имеет внешнюю связь по умолчанию; использование "extern" в порядке, но избыточно.
(Если требуется статическая привязка, функция должна быть объявлена как "статическая" как в своем прототипе, так и в заголовке функции, и они обычно должны быть в одном файле .c).
Много лет спустя я открываю этот вопрос. Прочитав все ответы и комментарии, я мог бы прояснить несколько деталей... Это может быть полезно для людей, которые попадают сюда через поиск очков.
Вопрос конкретно в использовании "внешних" функций, поэтому я буду игнорировать использование "extern" с глобальными переменными.
Позвольте определить 3 прототипа функций
//--------------------------------------
//Filename: "my_project.H"
extern int function_1(void);
static int function_2(void);
int function_3(void);
Файл заголовка может использоваться основным исходным кодом, как показано ниже.
//--------------------------------------
//Filename: "my_project.C"
#include "my_project.H"
void main(void){
int v1 = function_1();
int v2 = function_2();
int v3 = function_3();
}
int function_2(void) return 1234;
Чтобы скомпилировать и связать ссылку, мы должны определить "function_2" в том же файле исходного кода, где мы вызываем эту функцию. Две другие функции могут быть определены в другом исходном коде ".C" или они могут быть расположены в любом двоичном файле (.OBJ, *.LIB, *.DLL), для которого у нас может не быть исходного кода.
Давайте снова включим заголовок "my_project.H" в другой файл "*.C", чтобы лучше понять разницу. В том же проекте мы добавим следующий файл // --------------------------------------
//Filename: "my_big_project_splitted.C"
#include "my_project.H"
void old_main_test(void){
int v1 = function_1();
int v2 = function_2();
int v3 = function_3();
}
int function_2(void) return 5678;
int function_1(void) return 12;
int function_3(void) return 34;
Важные функции для уведомления: Когда функция определена как "статическая" в файле заголовка, компилятор/компоновщик должен найти экземпляр функции с этим именем в каждом модуле, который использует этот файл include.
Функция, которая является частью библиотеки C, может быть заменена только одним модулем, переопределяя прототип с "статическим" только в этом модуле. Например, замените любой вызов "malloc" и "free", чтобы добавить функцию обнаружения утечки памяти.
Спецификатор "extern" на самом деле не нужен для функций. Когда "статический" не найден, функция всегда считается "extern" .
Однако "extern" не является значением по умолчанию для переменных. Обычно любой заголовочный файл, определяющий переменные, видимые во многих модулях, должен использовать "extern" . Единственным исключением было бы, если заголовочный файл гарантированно будет включен из одного и только одного модуля.
Многие диспетчер проектов потребуют, чтобы такая переменная была помещена в начало модуля, а не внутри любого файла заголовка. Некоторые крупные проекты, такие как эмулятор видеоигр "Mame", даже требуют, чтобы такая переменная отображалась только над первой функцией, использующей их.
Очень хорошая статья о том, что я пришел с ключевым словом extern
, а также с примерами: http://www.geeksforgeeks.org/understanding-extern-keyword-in-c/
Хотя я не согласен с тем, что использование extern
в объявлениях функций является избыточным. Предполагается, что это настройка компилятора. Поэтому я рекомендую использовать extern
в объявлениях функций, когда это необходимо.
Если каждый файл в вашей программе сначала скомпилирован в объектный файл, тогда объектные файлы связаны между собой, вам нужно extern
. Он сообщает компилятору "Эта функция существует, но код для нее находится где-то в другом месте. Не паникуйте".
Все декларации функций и переменных в файлах заголовков должны быть extern
.
Исключения из этого правила являются встроенными функциями, определенными в заголовке, и переменными, которые, хотя и определены в заголовке, должны быть локальными для единицы перевода (исходный файл, в который заголовок включается): они должны быть static
.
В исходных файлах extern
не следует использовать для функций и переменных, определенных в файле. Просто префикс локальных определений с помощью static
и ничего не делайте для общих определений - по умолчанию они будут внешними символами.
Единственная причина использовать extern
вообще в исходном файле - объявлять функции и переменные, которые определены в других исходных файлах и для которых не предоставляется файл заголовка.
Объявление прототипов функций extern
фактически не нужно. Некоторым людям это не нравится, потому что они просто теряют пространство, а декларации функций уже имеют тенденцию к превышению лимитов линии. Другие любят это, потому что таким образом функции и переменные можно трактовать одинаково.
extern
необязателен для объявлений функций, но мне нравится обрабатывать переменные и функции одинаково - по крайней мере, это наиболее разумная вещь, которую я могу придумать, поскольку я точно не помню, почему я начал это делать;)
Функции, фактически определенные в других исходных файлах, должны быть объявлены только в заголовках. В этом случае вы должны использовать extern при объявлении прототипа в заголовке.
В большинстве случаев ваши функции будут одно из следующих (более похоже на лучшую практику):
Если у вас есть эта функция, определенная на другой dll или lib, чтобы компилятор отследил компоновщика, чтобы найти его. Типичный случай - когда вы вызываете функции из API OS.
Экстерн: "ключевое слово extern по умолчанию представляет перед любым объявлением переменной.Компилятор скрывает его.Когда вы хотите объявить функцию и она определена где-то в файле проекта, в таких условиях extern играет хорошую роль. Например, просто рассмотрите сокет программы, вы используете #include.Если вы пользователь Linux, найдите файл socket.h в своем компьютере и откройте его. Вы можете использовать множество объявлений функций extern. Это означает, что ваш заголовок" socket.h" объявлен всем требуемые функции, которые будут загружаться при запуске компиляции.Эти функции extern определены где-то в двоичных файлах сокетов.Еще нужно просто включить заголовочный файл. Ответственность линкера за загрузку двоичного файла для выполнения.
Все вышеприведенные примеры рассказывают об использовании, я просто перешел на уровень ОС.