Во-первых, компилятор не всегда выполняет встроенные функции, помеченные как inline
; Например, если вы отключите все оптимизации, они, вероятно, не будут встроены в них.
Когда вы определяете встроенную функцию
inline void do_something(void)
{
blah
}
и используйте эту функцию, даже в одном и том же файле, вызов этой функции разрешается компоновщиком, а не компилятором, поскольку это косвенно "extern". Но одно только это определение не дает внешнего определения функции.
Если включить декларацию без inline
void do_something(void);
в файле C, который может видеть определение inline
, компилятор предоставит внешнее определение функции, и ошибка должна исчезнуть.
Причина, по которой работает static inline
, заключается в том, что она делает функцию видимой только внутри этого модуля компиляции и, таким образом, позволяет компилятору разрешить вызов функции (и оптимизировать ее) и выдать код для функции в этом модуле компиляции. , Тогда компоновщик не должен разрешать его, поэтому нет необходимости во внешнем определении.
Лучше всего поместить встроенную функцию в файл заголовка и объявить их static inline
. Это устраняет необходимость во внешнем определении, поэтому решает проблему компоновщика. Однако это приводит к тому, что компилятор генерирует код для функции в каждом модуле компиляции, который его использует, что может привести к раздуванию кода. Но так как функция встроенная, она в любом случае, вероятно, мала, поэтому обычно это не проблема.
Другой вариант: определить как extern inline
в заголовке, а в одном файле C указать extern
объявление без модификатора inline
.
Руководство gcc объясняет это так:
Объявляя встроенную функцию, вы можете указать GCC совершать вызовы
эта функция быстрее. Одним из способов достижения этой цели GCC является интеграция
код этой функции в код для ее вызывающих. Это делает
выполнение быстрее за счет устранения накладных расходов на вызовы функций; в
Кроме того, если любое из фактических значений аргумента является постоянным, их
известные значения могут позволить упрощения во время компиляции, чтобы не
весь код встроенной функции должен быть включен. Эффект на
размер кода менее предсказуем; код объекта может быть больше или меньше
с функцией встраивания, в зависимости от конкретного случая. Вы можете
также направьте GCC, чтобы попытаться интегрировать все «достаточно простые» функции в
их звонящие с опцией -finline-functions
.
GCC реализует три разные семантики объявления функции
в соответствии. Один доступен с -std=gnu89
или -fgnu89-inline
или
когда атрибут gnu_inline
присутствует во всех встроенных объявлениях,
другой когда -std=c99
, -std=c1x
, -std=gnu99
или -std=gnu1x
(без -fgnu89-inline
), а третий используется при компиляции C ++.
Чтобы объявить функцию встроенной, используйте ключевое слово inline
в ее
объявление, как это:
static inline int
inc (int *a)
{
return (*a)++;
}
Если вы пишете файл заголовка для включения в программы ISO C90,
напишите __inline__
вместо inline
.
Три типа встраивания ведут себя одинаково в двух важных случаях:
когда ключевое слово inline
используется в функции static
, например,
пример выше, и когда функция впервые объявлена без использования
inline
ключевое слово и затем определяется с помощью inline
, например:
extern int inc (int *a);
inline int
inc (int *a)
{
return (*a)++;
}
В обоих этих общих случаях программа ведет себя так же, как если бы вы
не использовал ключевое слово inline
, за исключением его скорости.
Когда функция является одновременно встроенной и static
, если все вызовы
Функция интегрирована в вызывающего, и адрес функции
никогда не используется, то собственный код ассемблера функции никогда не будет
ссылки. В этом случае GCC фактически не выводит код ассемблера
для функции, если вы не укажете опцию
-fkeep-inline-functions
. Некоторые звонки не могут быть интегрированы для различных
причины (в частности, вызовы, предшествующие определению функции
не могут быть интегрированы, и рекурсивные вызовы в
определение). Если есть неинтегрированный вызов, то функцияскомпилирован в ассемблерный код как обычно.Функция также должна быть скомпилирована как обычно, если программа ссылается на свой адрес, потому что он не может быть встроенным.
Обратите внимание, что некоторые использования в определении функции могут сделать ее непригодной для встроенной замены.Среди этих применений: использование varargs, использование alloca, использование типов данных переменного размера, использование вычисленного goto, использование нелокального goto и вложенных функций.Использование -Winline
предупредит, когда функция, помеченная inline
, не может быть заменена, и даст причину сбоя.
В соответствии с требованиями ISO C ++, GCC рассматривает функции-члены, определенные в теле класса.быть помечены как встроенные, даже если они явно не объявлены с ключевым словом inline
.Вы можете переопределить это с помощью -fno-default-inline
.
. GCC не включает какие-либо функции без оптимизации, если вы не укажете для этой функции атрибут always_inline
, например:
/* Prototype. */
inline void foo (const char) __attribute__((always_inline));
Остатокэтого раздела относится к встраиванию в GNU C90.
Если встроенная функция не static
, то компилятор должен предполагать, что могут быть вызовы из других исходных файлов;поскольку глобальный символ может быть определен только один раз в любой программе, функция не должна быть определена в других исходных файлах, поэтому вызовы в них не могут быть интегрированы.Поэтому встроенная функция, отличная от static
, всегда компилируется сама по себе обычным способом.
Если в определении функции указать inline
и extern
, то определение используется только длявстраивание.Ни в коем случае функция не компилируется сама по себе, даже если вы явно ссылаетесь на ее адрес.Такой адрес становится внешней ссылкой, как если бы вы только объявили функцию и не определили ее.
Эта комбинация inline
и extern
почти не влияет на макрос.Способ его использования - поместить определение функции в заголовочный файл с этими ключевыми словами и поместить еще одну копию определения (без inline
и extern
) в библиотечный файл.Определение в заголовочном файле приведет к тому, что большинство вызовов функции будут встроенными.Если какое-либо использование функции останется, они будут ссылаться на одну копию в библиотеке.