Существуют разные способы создания определений встраиваемых функций. В стандарте говорится о том, что функция с внутренним связыванием может быть сделана встраиваемой, и данное определение для встраиваемой функции должно находиться в том же файле, где функция применяется. Поэтому простой подход предполагает использование спецификатора функции inline наряду со спецификатором класса хранения static. Как правило, встраиваемые функции определяются до их первого применения в файле, так что определение действует также и в качестве прототипа. Другими словами, код будет выглядеть примерно так:

Встретив встраиваемое объявление, компилятор может, к примеру, заменить вызов функции eatline() ее телом. Это значит, что результат может быть такой, как если бы вы взамен написали следующий код:
#include
inline static void eatline() // встраиваемое определение/прототип
{
while (getchar() != '\n') continue;
}
Препроцессор и библиотека С 689

Поскольку встраиваемая функция не имеет отдельного предназначенного для нее блока кода, получить ее адрес нельзя. (В действительности это возможно, но тогда компилятор сгенерирует функцию, отличную от встраиваемой.) Кроме того, встраиваемая функция может быть не видна в отладчике.
Встраиваемая функция должна быть короткой. Для длинной функции время, затрачиваемое на ее вызов, невелико по сравнению со временем выполнения тела функции, поэтому использование встраиваемой функции не обеспечит существенной экономии времени.
Для проведения оптимизаций по встраиванию функции компилятору должно быть известно содержимое определения функции. Это означает, что определение встраиваемой функции должно находиться в том же файле, что и ее вызов. По данной причине встраиваемая функция обычно имеет внутреннее связывание. Следовательно, если программа состоит из нескольких файлов, встраиваемое определение понадобится поместить в каждый файл, который вызывает функцию. Для достижения такого условия проще всего указать определение встраиваемой функции в заголовочном файле и затем включать этот файл в файлы, где функция применяется.

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

690 глава 16

Первый файл содержит определение inline static, как и ранее. Второй файл имеет определение обычной функции, отсюда и наличие внешнего связывания. Третий файл включает определение inline, в котором не указан квалификатор
static.
Что здесь происходит? Функция spam ( ) в file2.c использует определение square() из этого файла. Данное определение, имея внешнее связывание, является видимым другим файлам, но main() в filel. с применяет локальное определение static функции square(). Поскольку это определение также inline, компилятор может (или нет) оптимизировать код, возможно, встроив его. Наконец, для file3.c компилятор свободен в использовании либо встраиваемого определения из file3.c, либо определения с внешним связыванием из file2 .с (или обоих!). Если вы не укажете static в определении inline, как в файле file3.c, то определение inline рассматривается в качестве альтернативы, которая могла бы применяться вместо внешнего определения.
Обратите внимание, что до появления С99 встраиваемые функции в GCC были реализованы с использованием несколько отличающихся правил, так что интерпретация GCC спецификатора inline может зависеть от указанных флагов компилятора.
ФУНКЦИИ _Noreturn (С11)
Когда в стандарте С99 появилось ключевое слово inline, оно было единственным примером спецификатора функции. (Ключевые слова extern и static называются спецификаторами класса хранения и могут применяться к объектам данных, а также к функциям.) В стандарт С11 был добавлен второй спецификатор функции, _Noreturn, предназначенный для указания функции, которая по завершении не возвращает управление вызывающей функции. Примером функции _Noreturn является exit(); после обращения к ней вызывающая функция никогда не возобновит свое выполнение. Обратите внимание, что это отличается от возвращаемого типа void. Типичная функция void возвращает управление вызывающей функции; она просто не предоставляет какое-либо значение.
Читать дальше