Причина, по которой вы получаете ошибку компоновщика в этой программе, заключается в том, что ваша программа использует функцию maxf
, которая находится в математической библиотеке, и вы не связываетесь с -lm
. Это происходит только при определенных уровнях оптимизации.
Обратите внимание, что компилятор может принять решение не использовать ни inline, ни вызвать maxf
, потому что он может видеть, что переменная max
не используется, и, следовательно, фактически сохраняет значение в этом нет необходимости. При условии, что компилятор знает, что maxf
не имеет видимых побочных эффектов (и он знает, что, поскольку maxf
является стандартной библиотечной функцией), он может поэтому исключить присваивание max
и, следовательно, вызов maxf
. Фактически, компилятор может даже знать, какова семантика maxf
, и избежать вызова путем предварительного вычисления во время компиляции.
Чтобы избежать этого случая в своем тесте, вы должны выполнить оба следующих :
- Измените имя
maxf
, чтобы оно не совпадало с именем стандартной функции библиотеки. (Ниже я предполагаю, что вы переименовали его в myMax
. - . Либо объявите переменную
max
как volatile
, либо используйте ее непредсказуемым образом, чтобы хранилище произошло.
Вы также должны исправить прототип своей функции, поскольку она вызывается с аргументом double
.
Теперь давайте предположим, что вы соответствующим образом исправили свой тест и скомпилировали его. будет по-прежнему получать ошибку компоновщика при определенных уровнях оптимизации, потому что без оптимизации (или в случае Clang, при уровне оптимизации менее 2) встраивание не выполняется. (В G CC на уровне -O1 функция только встроенный, потому что в модуле перевода есть только один вызов. Если бы он был вызван в двух местах, вам понадобился бы уровень оптимизации 2 для запуска встраивания, как в Clang.)
Но ясно, что есть определение maxf
(или, в исправленном коде, myMax
или что-то подобное). Так почему компилятор не может его использовать?
Ответ связан с фактической семантикой inline
, что может помочь ответить на ваш первый вопрос. Как мы все знаем (и можем видеть в длительном обсуждении выше), компилятор принимает решение о встраивании функций более или менее независимо от предложения программиста в спецификаторе inline
. (Сравните это с устаревшим спецификатором register
, который компиляторы долгое время игнорировали.)
Но inline
имеет важное значение. Хотя компилятор может выяснить, основываясь на своих собственных эвристических, аналитических и оптимизационных параметрах кода, является ли хорошей идеей встроить функцию, он не может знать, каковы ваши намерения в отношении использования этой функции в других единицах перевода, которые будут связаны с созданием исполняемого файла.
Возможно, в каком-то другом модуле перевода функция myMax
вызывается как внешняя функция. В этом случае компилятор должен будет включать в себя компиляцию myMax
независимо от того, решил он встроить все варианты использования в этом файле.
С другой стороны, возможно, что myMax
предназначен для встроенный в каждую единицу перевода, в которой он появляется; другими словами, все единицы перевода включают определение myMax
. Но это может привести к проблеме, потому что эти различные определения столкнутся, когда единицы перевода будут связаны друг с другом, что приведет к другой ошибке компоновщика. (Дублирующее определение имени.) Вы можете обойти это, объявив функцию static
, но в этом случае вы можете обнаружить, что каждый из модулей в вашем исполняемом файле имеет свое собственное определение функции c, потому что компилятор выбрал не встроить его в эти модули. Это может значительно увеличить размер исполняемого файла.
И вот тут появляется объявление inline
. Объявление функции inline
означает «это определение для использования в тех случаях, когда эта функция используется встраиваемый ». Если компилятор видит функцию, объявленную inline
без явного объявления extern
, он не выдает определение функции , даже если он решает не включать ее . Он полагается на функцию, определяемую в некотором внешнем модуле перевода.
Таким образом, вы можете безопасно поместить объявление inline
в заголовочный файл. Это гарантирует, что все встроенные функции используют одно и то же определение, не вызывая проблем с повторяющимися именами в компоновщике. Но вам все равно нужно убедиться, что функция имеет определение ровно в одной единице перевода. (Это несколько похоже на связанную с этим проблему объявления глобальных переменных, используемых различными единицами перевода в программе. Глобальная переменная должна объявляться последовательно во всех единицах перевода, но определяться только в одной.)
В случае из inline
функций, вы указываете, что определение функции обязательно должно быть скомпилировано с использованием замедления extern
. Если вы объявили myMax
как inline
в своем заголовочном файле, вы можете удовлетворить это требование, добавив в ровно один файл реализации:
#include "myMax.h"
extern double myMax(double a, double b);
Обратите внимание, что вы определение не нужно - будет использоваться тот, что в заголовке, - но вам нужно получить правильный прототип.