Как правильно использовать ключевое слово extern в C - PullRequest
218 голосов
/ 30 января 2009

Мой вопрос о том, когда на функцию следует ссылаться с ключевым словом extern в C.

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

Я мог бы подумать о том, как extern работает неправильно, и если так, пожалуйста, поправьте меня.

Редактировать: Должны ли вы extern что-то, когда это объявление по умолчанию без ключевого слова в заголовочном файле?

Ответы [ 10 ]

269 голосов
/ 30 января 2009

"extern" меняет связь. С ключевым словом предполагается, что функция / переменная доступна где-то еще, а разрешение откладывается до компоновщика.

Существует разница между "extern" в функциях и в переменных: для переменных она не создает экземпляр самой переменной, то есть не выделяет никакой памяти. Это должно быть сделано где-то еще. Поэтому важно, если вы хотите импортировать переменную из другого места. Для функций это только говорит компилятору, что связь внешняя. Поскольку это значение по умолчанию (вы используете ключевое слово «static», чтобы указать, что функция не связана с использованием внешней связи), вам не нужно использовать это явно.

185 голосов
/ 31 января 2009

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 .

28 голосов
/ 31 января 2009

Уже указывалось, что ключевое слово extern является избыточным для функций.

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

14 голосов
/ 30 января 2009

В C 'extern' подразумевается для прототипов функций, так как прототип объявляет функцию, которая определена где-то еще. Другими словами, прототип функции имеет внешнюю связь по умолчанию; использование 'extern' хорошо, но излишне.

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

12 голосов
/ 17 декабря 2016

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

Вопрос конкретно об использовании функций "extern", поэтому я буду игнорировать использование "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;

Важные особенности, на которые следует обратить внимание: Когда функция определена как «статическая» в заголовочном файле, компилятор / компоновщик должен найти экземпляр функции с таким именем в каждом модуле, который использует этот включаемый файл.

Функция, которая является частью библиотеки C, может быть заменена только в одном модуле путем переопределения прототипа со «статическим» только в этом модуле. Например, замените любой вызов «malloc» и «free», чтобы добавить функцию обнаружения утечек памяти.

Спецификатор extern на самом деле не нужен для функций. Когда «static» не найдено, функция всегда считается «extern».

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

Многие менеджеры проектов тогда требуют, чтобы такая переменная была помещена в начале модуля, а не внутри какого-либо заголовочного файла. Некоторые крупные проекты, такие как эмулятор видеоигры "Mame", даже требуют, чтобы такая переменная появлялась только над первой функцией, использующей их.

8 голосов
/ 16 января 2014

Очень хорошая статья о ключевом слове extern, а также примеры: http://www.geeksforgeeks.org/understanding-extern-keyword-in-c/

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

5 голосов
/ 30 января 2009

Если каждый файл в вашей программе сначала компилируется в объектный файл, то объектные файлы связаны друг с другом, вам нужно extern. Он сообщает компилятору: «Эта функция существует, но код для нее находится где-то еще. Не паникуйте».

4 голосов
/ 30 января 2009

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

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

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

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


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

2 голосов
/ 30 января 2009

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

В большинстве случаев ваши функции будут одной из следующих (больше похоже на лучшую практику):

  • static (нормальные функции, которые не видно за пределами этого .c файла)
  • статическая строка (строка из .c или .h файлы)
  • extern (объявление в заголовках следующий вид (см. ниже)
  • [без ключевого слова вообще] (нормально функции предназначены для доступа с помощью внешние объявления)
2 голосов
/ 30 января 2009

Когда эта функция определена в другой dll или lib, так что компилятор обращается к компоновщику, чтобы найти ее. Типичный случай, когда вы вызываете функции из API ОС.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...