Введение

Функция в языках программирования C и C++
Встроенные функции в языках программирования C и C++

В языках программирования C и C++ встроенная функция – это функция, объявленная с ключевым словом `inline`. Это служит двум целям: во-первых, это директива компилятору, предлагающая (но не требующая) компилятору подставлять тело функции непосредственно в место вызова, выполняя подстановку встроенного кода, то есть вставляя код функции по адресу каждого вызова, тем самым избегая накладных расходов, связанных с вызовом функции. В этом отношении это аналогично спецификатору класса хранения `register`, который также предоставляет рекомендацию по оптимизации. Во-вторых, `inline` изменяет поведение связывания; детали этого сложны. Это необходимо из-за модели раздельной компиляции и связывания в C/C++, в частности, потому что определение (тело) функции должно быть продублировано во всех единицах трансляции, где оно используется, чтобы обеспечить возможность встроенной подстановки во время компиляции. Если функция имеет внешнюю связь, это может привести к конфликту при связывании (нарушению уникальности внешних символов). C и C++ (а также диалекты, такие как GNU C и Visual C++) решают эту проблему различными способами.

С99

В C99 функция, определенная как inline, никогда не будет, а функция, определенная как extern inline, всегда будет генерировать внешне видимую функцию. В отличие от C++, нет способа запросить генерацию внешне видимой функции, совместно используемой между единицами трансляции, только при необходимости. Если inline-объявления смешиваются с extern inline-объявлениями или с неквалифицированными объявлениями (то есть без inline-квалификатора или класса хранения), то единица трансляции должна содержать определение (независимо от того, является ли оно неквалифицированным, inline или extern inline), и для него будет сгенерирована внешне видимая функция. Для функции, определенной как inline, требуется ровно одно определение этой функции где-либо еще в программе, которое либо определено как extern inline, либо без квалификатора. Если в программе предоставлено более одного такого определения, компоновщик сообщит об ошибке дублирования символов. Если же его нет, компоновщик не обязательно выдаст ошибку, поскольку, если все использования могут быть подставлены inline, оно не требуется. Однако он может выдать ошибку, так как компилятор всегда может проигнорировать inline-квалификатор и генерировать вызовы функции вместо этого, как обычно происходит, если код компилируется без оптимизации. (Это может быть желаемым поведением, если функция должна быть подставлена inline везде любыми средствами, и должна быть сгенерирована ошибка, если это не так.) Удобный способ — определить inline-функции в заголовочных файлах и создать один c-файл для каждой функции, содержащий extern inline-объявление для нее и включающий соответствующий заголовочный файл с определением. Не имеет значения, идет ли объявление до или после включения. Чтобы предотвратить добавление недостижимого кода в окончательный исполняемый файл, если все использования функции были подставлены inline, рекомендуется, за исключением того, что gnu89 разрешает переопределять extern inline-функцию как неквалифицированную функцию, в то время как C99 inline этого не делает. Таким образом, gnu89 extern inline без переопределения аналогичен C99 inline, а gnu89 inline аналогичен C99 extern inline; другими словами, в gnu89 функция, определенная как inline, всегда будет, а функция, определенная как extern inline, никогда не будет генерировать внешне видимую функцию. Это объясняется тем, что это соответствует переменным, для которых память никогда не резервируется, если они определены как extern, и всегда резервируется, если они определены без. Обоснование C99, напротив, заключается в том, что было бы удивительно, если бы использование inline имело побочный эффект — всегда генерировать не подставленную inline версию функции, что противоречит тому, что предполагает ее название. Замечания для C99 о необходимости предоставления ровно одного внешне видимого экземпляра функции для inline-функций и о возникающей в результате проблеме с недостижимым кодом применимы mutatis mutandis и к gnu89. gcc до и включая версию 4.2 использовал семантику gnu89, даже когда std=c99 был явно указан. Начиная с версии 5, это соответствует традиционному поведению компиляторов Unix C для нескольких не-extern определений неинициализированных глобальных переменных.