C ++ ключевое слово extern для функций. Почему бы просто не включить заголовочный файл? - PullRequest
43 голосов
/ 09 апреля 2010

Если я правильно понимаю, это означает

extern void foo();

что функция foo объявлена ​​в другой единице перевода.

1) Почему бы просто #include заголовок, в котором объявлена ​​эта функция?

2) Как компоновщик узнает, где искать функцию во время компоновки?

edit: Может быть, я должен уточнить, что за приведенным выше объявлением следует функция

foo();

Он никогда не определяется в этой единице перевода.

Ответы [ 5 ]

27 голосов
/ 09 апреля 2010

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

2) Компоновщик просматривает все объектные файлы и библиотеки, о которых ему было сказано, чтобы найти функции и другие символы.

15 голосов
/ 09 апреля 2010

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

Существует еще одно использование ключевого слова extern, которое выглядит так:

extern "C" void foo();

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

15 голосов
/ 09 апреля 2010

Нет, это означает, что функция foo объявлена ​​с внешней связью . Внешняя связь означает, что имя foo относится к одной и той же функции во всей программе. Где определена функция, не имеет значения. Это можно определить в этом блоке перевода. Это можно определить в другой единице перевода.

Использование ключевого слова extern, как показано в вашем примере, является излишним. Функции всегда имеют внешнюю связь по умолчанию. Выше 100% эквивалентно просто

void foo();

Что касается компоновщика, то, когда компоновщик связывает программу вместе, он просто выглядит везде . Он просматривает все определения, пока не найдет определение для foo.

8 голосов
/ 09 апреля 2010

Это уже означает, что без ключевого слова extern. По умолчанию функции имеют внешнюю связь, если вы не объявили их статическими.

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

2 голосов
/ 09 апреля 2010

1) Я не знаю, зачем мне это нужно для функции. Может быть, кто-то еще может вмешаться.

2) Компоновщик определяет это, просматривая все объектные файлы и проверяя символы внутри каждого объектного файла. Я предполагаю, что в зависимости от вашего компоновщика, точный порядок поиска может отличаться.

Для GNU binutils 'ld все объектные файлы и библиотеки, которые появляются в командной строке компоновщика после поиска объекта, содержащего пропущенный символ, слева направо и выбирается первый найденный символ.

Пример 1:

  • a.o - использует foo (), bar ()
  • liba - обеспечивает бар ()
  • libb - предоставляет foo ()

$> ld a.o -la -lb

приведет к поиску неопределенных символов. После этого ld будет проходить по библиотекам слева направо для поиска этих символов и найдет bar в liba и foo в libb.

Это может привести к странным проблемам при циклических зависимостях:

Пример 2:

  • a.o - использует bar ()
  • liba - предоставляет bar (), использует foo ()
  • libb - предоставляет foo (), использует bar ()

Теперь существует круговая зависимость между liba и libb, и соединение не будет выполнено:

$> ld a.o -la -lb

потому что при поиске неопределенных символов в libb ld определит, что нет другой lib справа от -lb , предоставляющей этот символ. Это можно исправить как минимум двумя способами:

1) дважды связать liba: $> ld a.o -la -lb -la

2) использовать функцию группировки ld $> ld a.o --start-group -la -lb --end-group

В случае 2) группа указывает ld выполнить поиск по всем символам во всех библиотеках этой группы.

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