Если я понимаю это правильно, это означает
extern void foo();
то, что функциональное нечто объявляется в другой единице перевода.
1) Почему не только #include заголовок, в котором объявляется эта функция?
2) Как компоновщик знает, где искать функцию при соединении времени?
править: Возможно, я должен разъяснить, что вышеупомянутое объявление затем сопровождается при помощи функции
foo();
Это никогда не определяется в этой единице перевода.
1) Может не иметь файла заголовка. Но да, как правило, для больших проектов у вас должен быть файл заголовка, если несколько единиц перевода собираются использовать эту функцию (не повторяйтесь).
2) Компоновщик просматривает все объектные файлы и библиотеки, о которых ему было сказано, чтобы найти функции и другие символы.
Нет, это означает, что функция foo
объявлена с внешней связью . Внешняя связь означает, что имя foo
относится к одной и той же функции во всей программе. Не имеет значения, где определена функция. Его можно определить в этой единице перевода. Его можно определить в другой единице перевода.
Использование ключевого слова extern
, как показано в вашем примере, излишне. По умолчанию функции всегда имеют внешнюю связь. Вышеупомянутое на 100% эквивалентно просто
void foo();
. Что касается компоновщика, когда компоновщик связывает программу вместе, он просто смотрит везде . Он просматривает все определения, пока не найдет определение для foo
.
Как уже было сказано другими, ключевое слово extern
используется, чтобы указать, что имя (переменная или функция) имеет внешнюю связь, то есть имя относится к одному и тому же объекту во всей программе. Кроме того, это значение по умолчанию для переменных и функций, определенных в области файла, поэтому такое использование излишне.
Существует еще одно использование ключевого слова extern, которое выглядит следующим образом:
extern "C" void foo();
Это означает, что функция foo
будет связана с использованием соглашений C для связывания (возможно, потому что это функция, определенная в библиотеке C. или функция, предназначенная для вызова программами на языке C).
Это уже означает, что без ключевого слова extern. По умолчанию функции имеют внешнюю связь, если вы не объявляете их статическими.
Использование прототипов функций - это нормально, но легко ошибиться. Ошибка компоновщика, которую вы получите, не так легко диагностировать, когда вы переопределяете реализацию функции. Компоновщик не знает, где искать, ваша задача - предоставить ему объектный файл, содержащий определение функции, чтобы он оставался довольным.
1) Я не знаю, зачем мне это для функции. Может быть, вмешается кто-нибудь другой.
2) Компоновщик определяет это, просматривая все объектные файлы и проверяя символы внутри каждого объектного файла. Я предполагаю, что в зависимости от вашего компоновщика точный порядок поиска может отличаться.
Для GNU binutils ld все объектные файлы и библиотеки, которые появляются в командной строке компоновщика после объекта, содержащего отсутствующий символ, ищутся слева направо и выбирается первый найденный символ.
Пример 1:
$> ld ao -la - lb
приведет к поиску неопределенных символов в ao. После этого ld будет просматривать библиотеки слева направо для поиска этих символов и найдет bar в liba и foo в libb.
Это может привести к странным проблемам с циклическими зависимостями:
Пример 2:
Теперь существует циклическая зависимость между liba и libb, и связывание не удастся:
$> ld ao -la -lb
потому что при поиске неопределенных символов в libb, ld определит, что нет другой библиотеки справа от -lb , которая предоставляет этот символ. Это можно исправить как минимум двумя способами:
1) дважды связать liba: $> ld ao -la -lb -la
2) использовать функцию группировки ld $> ld ao --start-group -la -lb --end-group
В случае 2) группировка указывает ld на поиск по всем символам во всех библиотеках, принадлежащих этой группе.