6.7.4 Спецификаторы функций
Новая особенность C99:inline
ключевое слово, заимствованы из C ++, является функцией спецификатор , который может быть использован только в объявлении функции. Это полезно для оптимизации программ, которые требуют, чтобы определение функции было видимым на месте вызова. (Обратите внимание, что Стандарт не пытается указать характер этих оптимизаций.)
Видимость обеспечивается, если функция имеет внутреннюю связь или внешнюю связь, и вызов находится в той же единице перевода, что и внешнее определение. В этих случаях присутствие
inline
ключевого слова в объявлении или определении функции не имеет никакого эффекта, кроме указания предпочтения, что вызовы этой функции должны быть оптимизированы по сравнению с вызовами других функций, объявленных без inline
ключевого слова.
Видимость - это проблема для вызова функции с внешней связью, когда вызов находится в единицах перевода, отличных от определения функции. В этом случаеinline
ключевое слово позволяет блоку перевода, содержащему вызов, также содержать локальное или встроенное определение функции.
Программа может содержать модуль перевода с внешним определением, модуль перевода со встроенным определением и модуль перевода с объявлением, но без определения функции. Вызовы в последней единице перевода будут использовать внешнее определение как обычно.
Считается, что встроенное определение функции отличается от внешнего определения. Если вызов какой-либо функции func
с внешней связью происходит там, где видно встроенное определение, поведение такое же, как если бы вызов был сделан к другой функции, скажем
__func
, с внутренней связью. Соответствующая программа не должна зависеть от того, какая функция вызывается. Это встроенная модель в стандарте.
Соответствующая программа не должна полагаться на реализацию, использующую встроенное определение, а также не может полагаться на реализацию, использующую внешнее определение. Адрес функции - это всегда адрес, соответствующий внешнему определению, но когда этот адрес используется для вызова функции, может использоваться встроенное определение. Следовательно, следующий пример может вести себя не так, как ожидалось.
inline const char *saddr(void)
{
static const char name[] = "saddr";
return name;
}
int compare_name(void)
{
return saddr() == saddr(); // unspecified behavior
}
Поскольку реализация может использовать встроенное определение для одного из вызовов saddr
и использовать внешнее определение для другого, не гарантируется, что результат операции равенства будет равен 1 (истина). Это показывает, что статические объекты, определенные во встроенном определении, отличаются от соответствующих им объектов во внешнем определении. Это мотивировало ограничение даже на определение неconst
объекта этого типа.
Встраивание было добавлено в Стандарт таким образом, чтобы его можно было реализовать с помощью существующей технологии компоновщика, а подмножество встраивания C99 совместимо с C ++. Это было достигнуто за счет требования, чтобы ровно одна единица перевода, содержащая определение встроенной функции, была указана как единица, которая обеспечивает внешнее определение функции. Поскольку эта спецификация состоит просто из объявления, в котором либо отсутствует inline
ключевое слово, либо содержится и то, inline
и другое extern
, оно также будет принято переводчиком C ++.
Встраивание в C99 действительно расширяет спецификацию C ++ двумя способами. Во-первых, если функция объявлена
inline
в одной единице трансляции, ее необязательно объявлять inline
во всех остальных единицах трансляции. Это позволяет, например, использовать библиотечную функцию, которая должна быть встроена в библиотеку, но доступна только через внешнее определение в другом месте. Альтернатива использования функции-оболочки для внешней функции требует дополнительного имени; и это также может отрицательно сказаться на производительности, если переводчик фактически не выполняет встроенную замену.
Во-вторых, требование, чтобы все определения встроенной функции были «точно такими же», заменяется требованием, чтобы поведение программы не зависело от того, реализован ли вызов с видимым встроенным определением или внешним определением функция. Это позволяет специализировать встроенное определение для его использования в конкретной единице перевода. Например, внешнее определение библиотечной функции может включать некоторую проверку аргументов, которая не требуется для вызовов, сделанных из других функций в той же библиотеке. Эти расширения действительно имеют некоторые преимущества; а программисты, заботящиеся о совместимости, могут просто соблюдать более строгие правила C ++.
Обратите внимание, что реализации не подходят для предоставления встроенных определений стандартных библиотечных функций в стандартные заголовки, потому что это может нарушить некоторый унаследованный код, который повторно объявляет стандартные библиотечные функции после включения их заголовков. inline
Ключевое слово предназначено только для предоставления пользователям переносимым способом , чтобы предложить встраивание функций. Поскольку стандартные заголовки не обязательно должны быть переносимыми, у реализаций есть другие варианты, такие как:
#define abs(x) __builtin_abs(x)
или другие непереносимые механизмы для встраивания стандартных библиотечных функций.