В C и C ++ языков программирования , встроенная функция является квалифицированным с ключевым словом inline
; это служит двум целям. Во-первых, он служит директивой компилятора, которая предлагает (но не требует), чтобы компилятор заменил тело встроенной функции, выполнив встроенное расширение , то есть вставив код функции в адрес каждого вызова функции, тем самым сэкономив накладные расходы на вызов функции. В этом отношении он аналогичен register
спецификатору класса хранения , который аналогично дает подсказку по оптимизации. [1] Вторая цельinline
заключается в изменении поведения связи; детали этого сложны. Это необходимо из-за раздельной модели компиляции + связывания C / C ++, в частности потому, что определение (тело) функции должно быть продублировано во всех единицах перевода, где она используется, чтобы позволить встраивание во время компиляции , что, если функция имеет внешнюю linkage , вызывает коллизию при связывании (нарушает уникальность внешних символов). C и C ++ (и такие диалекты, как GNU C и Visual C ++) решают эту проблему по-разному. [1]
Пример
inline
Функция может быть записана в C или C ++ , как это:
встроенный void swap ( int * m , int * n ) { int tmp = * m ; * м = * п ; * n = tmp ; }
Затем утверждение, подобное следующему:
своп ( & x , & y );
могут быть переведены в (если компилятор решит выполнить встраивание, что обычно требует включения оптимизации):
int tmp = x ; х = у ; у = tmp ;
При реализации алгоритма сортировки, выполняющего много замен, это может увеличить скорость выполнения.
Стандартная поддержка
C ++ и C99 , но не его предшественники K&R C и C89 , имеют поддержку inline
функций, хотя и с другой семантикой. В обоих случаях inline
не приводит к принудительному встраиванию; компилятор может решить не встраивать функцию вообще или только в некоторых случаях. Разные компиляторы различаются по степени сложности функции, которую они могут встроить. Основные компиляторы C ++, такие как Microsoft Visual C ++ и GCC, поддерживают параметр, который позволяет компиляторам автоматически встраивать любую подходящую функцию, даже если она не помечена как inline
функции. Однако просто опустить inline
ключевое слово, чтобы позволить компилятору принимать все решения по встраиванию, невозможно, поскольку компоновщик затем будет жаловаться на повторяющиеся определения в разных единицах перевода. Это связано с тем, что inline
не только дает компилятору подсказку о том, что функция должна быть встроена, но также влияет на то, будет ли компилятор генерировать вызываемую внешнюю копию функции (см. Классы хранения встроенных функций ).
Нестандартные пристройки
GNU C , как часть предлагаемого им диалекта gnu89, поддерживает inline
как расширение C89. Однако семантика отличается как от C ++, так и от C99. armcc в режиме C90 также предлагает inline
нестандартное расширение с семантикой, отличной от gnu89 и C99.
Некоторые реализации предоставляют средства, с помощью которых компилятор может встроить функцию, обычно с помощью спецификаторов объявления, зависящих от реализации:
- Microsoft Visual C ++:
__forceinline
- gcc или clang:
__attribute__((always_inline))
или__attribute__((__always_inline__))
, последний из которых полезен, чтобы избежать конфликта с определяемым пользователем макросом с именемalways_inline
.
Неизбирательное использование этого может привести к увеличению кода (раздутый исполняемый файл), минимальному увеличению производительности или ее отсутствию, а в некоторых случаях даже к снижению производительности. Более того, компилятор не может встроить функцию при любых обстоятельствах, даже если встраивание принудительно; в этом случае и gcc, и Visual C ++ генерируют предупреждения.
Принуждение к встраиванию полезно, если
inline
не соблюдается компилятором (игнорируется анализатором затрат / выгод компилятора), и- встраивание приводит к необходимому увеличению производительности
Для переносимости кода можно использовать следующие директивы препроцессора:
#ifdef _MSC_VER #define forceinline __forceinline #elif определен (__ GNUC__) #define forceinline инлайн __attribute __ ((__ always_inline__)) #elif определен (__ CLANG__) #if __has_attribute (__ always_inline__) #define forceinline инлайн __attribute __ ((__ always_inline__)) #else #define forceinline рядный #endif #else #define forceinline рядный #endif
Классы хранения встроенных функций
static inline
имеет одинаковые эффекты для всех диалектов C и C ++. При необходимости он выдаст локально видимую (внешнюю копию) функцию.
Независимо от класса хранения компилятор может игнорировать inline
квалификатор и генерировать вызов функции на всех диалектах C и C ++.
Влияние класса хранения, extern
когда он применяется или не применяется к inline
функциям, различается в диалектах C [2] и C ++. [3]
C99
В C99 определенная функция inline
никогда не будет, а определенная функция extern inline
всегда будет генерировать внешне видимую функцию. В отличие от C ++, нет способа запросить, чтобы внешне видимая функция, совместно используемая между единицами перевода, выдавалась только в случае необходимости.
Если inline
объявления смешаны с extern inline
объявлениями или с неквалифицированными объявлениями (т. inline
Е. Без квалификатора или класса хранения), единица трансляции должна содержать определение (независимо от того, является ли оно неквалифицированным inline
, или extern inline
), и для него будет создана внешне видимая функция.
Для определенной функции inline
требуется ровно одна функция с таким именем где-то еще в программе, которая либо определена, extern inline
либо не имеет квалификатора. Если во всей программе представлено более одного такого определения, компоновщик будет жаловаться на повторяющиеся символы. Если, однако, этого нет, компоновщик не обязательно будет жаловаться, потому что, если все варианты использования могут быть встроены, в этом нет необходимости. Но он может жаловаться, поскольку компилятор всегда может игнорировать inline
квалификатор и вместо этого генерировать вызовы функции, как обычно происходит, если код компилируется без оптимизации. (Это может быть желаемым поведением, если функция должна быть встроена повсюду, и если это не так, должна быть сгенерирована ошибка.) Удобный способ - определить inline
функции в файлах заголовков и создать один файл .c на функцию, содержащую ее extern inline
объявление и соответствующий заголовочный файл с определением. Не имеет значения, находится ли объявление до или после включения.
Чтобы предотвратить недостижимый код от добавления к конечному исполняемому файлу, если были встраиваемыми все виды использования функции, рекомендуется [3] , чтобы поместить объектные файлы всех таких .c файлов с одной extern inline
функции в статической библиотеки файлов, как правило , с ar rcs
, затем свяжите с этой библиотекой вместо отдельных объектных файлов. Это приводит к связыванию только тех объектных файлов, которые действительно необходимы, в отличие от непосредственного связывания объектных файлов, что приводит к тому, что они всегда включаются в исполняемый файл. Однако файл библиотеки должен быть указан после всех других объектных файлов в командной строке компоновщика, поскольку вызовы функций из объектных файлов, указанных после файла библиотеки, компоновщиком не учитываются. Вызовы от inline
функций к другим inline
функциям будут разрешены компоновщиком автоматически ( s
опция в ar rcs
гарантирует это).
Альтернативное решение - использовать оптимизацию времени компоновки вместо библиотеки. gcc предоставляет флаг -Wl,--gc-sections
для исключения разделов, в которых все функции не используются. Так будет с объектными файлами, содержащими код единственной неиспользуемой extern inline
функции. Однако он также удаляет любые и все другие неиспользуемые разделы из всех других объектных файлов, а не только те, которые относятся к неиспользуемым extern inline
функциям. (Может потребоваться связать в исполняемый файл функции, которые должны вызываться программистом из отладчика, а не самой программой, например, для проверки внутреннего состояния программы.) При таком подходе также возможно использовать один файл .c со всеми extern inline
функциями вместо одного файла .c для каждой функции. Затем файл должен быть скомпилирован с расширением -fdata-sections -ffunction-sections
. Однако страница руководства gcc предупреждает об этом, говоря: «Используйте эти параметры только тогда, когда это дает значительную выгоду».
Некоторые рекомендуют совершенно другой подход, заключающийся в определении функций как в файлах заголовков, а static inline
не inline
в файлах заголовков. [2] Тогда недоступный код не будет сгенерирован. Однако у этого подхода есть недостаток в противоположном случае: дублирующийся код будет сгенерирован, если функция не может быть встроена более чем в одну единицу трансляции. Выпущенный код функции не может использоваться совместно с единицами трансляции, потому что он должен иметь разные адреса. Это еще один недостаток; взятие адреса такой функции, определенной как static inline
файл заголовка, даст разные значения в разных единицах перевода. Следовательно, static inline
функции следует использовать только в том случае, если они используются только в одной единице перевода, что означает, что они должны переходить только в соответствующий файл .c, а не в файл заголовка.
gnu89
Семантика gnu89 inline
и extern inline
по существу полная противоположность семантике в C99, [4] за исключением того, что gnu89 разрешает переопределение extern inline
функции как неквалифицированную функцию, а C99 inline
- нет. [5] Таким образом, gnu89 extern inline
без переопределения подобен C99 inline
, а gnu89 inline
подобен C99 extern inline
; другими словами, в gnu89 определенная функция inline
всегда будет, а определенная функция extern inline
никогда не будет генерировать внешне видимую функцию. Причина этого в том, что он соответствует переменным, для которых никогда не будет зарезервировано хранилище, если оно определено как extern
и всегда, если определено без него. Суть C99, напротив, состоит в том, что было бы удивительно, если бы использование inline
имело побочный эффект - всегда выдавать не встроенную версию функции - что противоречит тому, что предполагает ее название.
Замечания для C99 о необходимости предоставить ровно один видимый извне экземпляр функции для встроенных функций и о возникающей проблеме с недоступным кодом применяются mutatis mutandis и к gnu89.
gcc до версии 4.2 включительно использовал inline
семантику gnu89, даже если она -std=c99
была явно указана. [6] В версии 5 [5] gcc переключился с gnu89 на диалект gnu11, эффективно включив inline
семантику C99 по умолчанию. Чтобы вместо этого использовать семантику gnu89, они должны быть включены явно, либо с помощью -std=gnu89
или, чтобы влиять только на встраивание -fgnu89-inline
, либо путем добавления gnu_inline
атрибута ко всем inline
объявлениям. Для того, чтобы обеспечить C99 семантика, либо -std=c99
, -std=c11
, -std=gnu99
или -std=gnu11
(без -fgnu89-inline
может быть использована). [3]
C ++
В C ++ определенная функция inline
при необходимости будет генерировать функцию, совместно используемую модулями перевода, обычно помещая ее в общий раздел объектного файла, для которого она необходима. Функция должна иметь одинаковое определение везде, всегда с inline
квалификатором. В C ++ extern inline
это то же самое, что inline
. Обоснование подхода C ++ заключается в том, что это наиболее удобный способ для программиста, поскольку не требуется никаких специальных мер предосторожности для устранения недостижимого кода, и, как и для обычных функций, не имеет значения, extern
указан он или нет.
inline
Классификатор автоматически добавляются к функции , определенной как часть определения класса.
armcc
ARMCC в режиме C90 обеспечивает extern inline
и inline
семантику , которые являются такими же , как и в C ++: Такие определения будут излучать функцию общего числа единиц перевода , если это необходимо. В режиме C99 extern inline
всегда генерирует функцию, но, как и в C ++, она будет совместно использоваться модулями перевода. Таким образом, одна и та же функция может быть определена extern inline
в разных единицах перевода. [7] Это соответствует традиционному поведению компиляторов Unix C [8] для множественных неопределений extern
неинициализированных глобальных переменных.
Ограничения
В inline
любом случае для получения адреса функции требуется код для не встроенной копии этой функции.
В C99 функция inline
or extern inline
не должна обращаться к static
глобальным переменным или определять нелокальные const
static
переменные. const static
локальные переменные могут быть или не быть разными объектами в разных единицах трансляции, в зависимости от того, была ли функция встроена или был сделан вызов. Только static inline
определения могут ссылаться на идентификаторы с внутренней связью без ограничений; это будут разные объекты в каждой единице перевода. В C ++ разрешены оба const
и нелокальные const
static
, и они относятся к одному и тому же объекту во всех единицах перевода.
gcc не может встраивать функции, если [3]
- они вариативны ,
- использовать
alloca
- использовать вычисленный
goto
- использовать нелокальный
goto
- использовать вложенные функции
- использовать
setjmp
- использовать
__builtin_longjmp
- использовать
__builtin_return
, или - использовать
__builtin_apply_args
Согласно спецификациям Microsoft в MSDN, MS Visual C ++ не может быть встроенным (даже с __forceinline
), если
- Функция или ее вызывающая сторона компилируются с / Ob0 (параметр по умолчанию для отладочных сборок).
- Функция и вызывающий объект используют разные типы обработки исключений ( обработка исключений C ++ в одном, обработка структурированных исключений в другом).
- Функция имеет список переменных аргументов .
- Функция использует встроенную сборку , если она не скомпилирована с / Og, / Ox, / O1 или / O2.
- Функция рекурсивная и не сопровождается
#pragma inline_recursion(on)
. С помощью прагмы рекурсивные функции встроены в глубину по умолчанию, равную 16 вызовам. Чтобы уменьшить глубину встраивания, используйтеinline_depth
прагму. - Функция виртуальная и вызывается виртуально. Могут быть встроены прямые вызовы виртуальных функций.
- Программа принимает адрес функции, и вызов осуществляется через указатель на функцию. Могут быть встроены прямые вызовы функций, адрес которых был занят.
- Функция также отмечена
__declspec
модификатором naked .
Проблемы
Помимо проблем со встроенным расширением в целом (см. Раздел "Встроенное расширение" § Влияние на производительность ), inline
функции как языковая функция могут быть не так полезны, как кажутся, по ряду причин:
- Часто компилятор находится в лучшем положении, чем человек, чтобы решить, следует ли встраивать конкретную функцию. Иногда компилятор не может встроить столько функций, сколько указывает программист.
- Важно отметить, что код (
inline
функции) открывается клиенту (вызывающей функции). - По мере развития функций они могут стать пригодными для встраивания там, где их не было раньше, или больше не подходить для встраивания там, где они были раньше. Хотя встраивание или отключение функции проще, чем преобразование в макрос и обратно, оно по-прежнему требует дополнительного обслуживания, которое обычно дает относительно небольшую выгоду.
- Встроенные функции, широко используемые в собственных системах компиляции на основе C, могут увеличивать время компиляции, поскольку промежуточное представление их тел копируется в каждый сайт вызова.
- Для спецификации
inline
C99 требуется ровно одно внешнее определение функции, если оно где-то используется. Если такое определение не было предоставлено программистом, это может легко привести к ошибкам компоновщика. Это может произойти при отключенной оптимизации, которая обычно предотвращает встраивание. С другой стороны, добавление определений может вызвать недостижимый код, если программист не избегает этого осторожно, помещая их в библиотеку для компоновки, используя оптимизацию времени компоновки илиstatic inline
. - В C ++ необходимо определить
inline
функцию в каждом модуле (единице перевода), который ее использует, тогда как обычная функция должна быть определена только в одном модуле. В противном случае было бы невозможно скомпилировать один модуль независимо от всех других модулей. В зависимости от компилятора это может привести к тому, что каждый соответствующий объектный файл будет содержать копию кода функции для каждого модуля с некоторым использованием, которое не может быть встроено. - Во встроенном программном обеспечении часто необходимо разместить определенные функции в определенных разделах кода с помощью специальных инструкций компилятора, таких как операторы «прагма». Иногда функции в одном сегменте памяти может потребоваться вызвать функцию в другом сегменте памяти, и если происходит встраивание вызываемой функции, то код вызываемой функции может оказаться в сегменте, где его быть не должно. Например, сегменты высокопроизводительной памяти могут быть очень ограничены в кодовом пространстве, и если функция, принадлежащая такому пространству, вызывает другую большую функцию, которая не предназначена для высокопроизводительной секции, и вызываемая функция оказывается неправильно встроенной, тогда это может привести к тому, что сегменту высокопроизводительной памяти не хватит места для кода. По этой причине иногда необходимо убедиться, что функции не становятся встроенными.
Кавычки
- "Объявление функции [...] со встроенным спецификатором объявляет встроенную функцию. Встроенный спецификатор указывает реализации, что встроенная подстановка тела функции в точке вызова предпочтительнее обычного механизма вызова функции. не требуется выполнять эту встроенную подстановку в точке вызова; однако, даже если эта встроенная подстановка опущена, другие правила для встроенных функций, определенные в 7.1.2, все равно должны соблюдаться. "
- - ISO / IEC 14882: 2011, текущий стандарт C ++, раздел 7.1.2.
- «Функция, объявленная со встроенным спецификатором функции, является встроенной функцией. [...] Создание встроенной функции предполагает, что вызовы функции должны быть как можно быстрее. Степень эффективности таких предложений определяется реализацией ( сноска: например, реализация может никогда не выполнять встроенную замену или может выполнять только встроенную замену для вызовов в области встроенного объявления. )
- "[...] Встроенное определение не предоставляет внешнего определения для функции и не запрещает внешнее определение в другой единице перевода . Встроенное определение предоставляет альтернативу внешнему определению, которое переводчик может использовать для реализации любого вызов функции в той же единице перевода. Не указано, использует ли вызов функции встроенное определение или внешнее определение ".
- - ISO 9899: 1999 (E), стандарт C99, раздел 6.7.4
Смотрите также
Рекомендации
- ^ a b Мейерс, Рэнди (1 июля 2002 г.). «Новый C: встроенные функции» . Цитировать журнал требует
|journal=
( помощь ) - ^ а б http://www.greenend.org.uk/rjk/tech/inline.html
- ^ а б в г https://gcc.gnu.org/onlinedocs/gcc-7.1.0/gcc/Inline.html
- ^ http://blahg.josefsipek.net/?p=529
- ^ a b https://gcc.gnu.org/gcc-5/porting_to.html
- ^ https://gcc.gnu.org/ml/gcc-patches/2007-02/msg00119.html
- ^ http://infocenter.arm.com/help/topic/com.arm.doc.faqs/ka15831.html
- ^ Страница руководства gcc, описание
-fno-common
- ЯНА ДЕБАСИШ (1 января 2005 г.). C ++ И ОБЪЕКТИВНОЕ ПРОГРАММИРОВАНИЕ . PHI Learning Pvt. ООО ISBN 978-81-203-2871-6.
- Сенгупта, Пробал (1 августа 2004 г.). Объектно-ориентированное программирование: основы и приложения . PHI Learning Pvt. ООО ISBN 978-81-203-1258-6.
- Свенк, Горан (2003). Объектно-ориентированное программирование: использование C ++ для инженерии и технологий . Cengage Learning. ISBN 0-7668-3894-3.
- Балагурусамы (2013). Объектно-ориентированное программирование на C ++ . Тата Макгроу-Хилл Образование. ISBN 978-1-259-02993-6.
- Кирх-Принц, Улла; Принц, Питер (2002). Полное руководство по программированию на C ++ . Джонс и Бартлетт Обучение. ISBN 978-0-7637-1817-6.
- Конгер, Дэвид (2006). Создание игр на C ++: пошаговое руководство . Новые всадники. ISBN 978-0-7357-1434-2.
- Скиннер, MT (1992). Продвинутая книга C ++ . Silicon Press. ISBN 978-0-929306-10-0.
- Любовь (1 сентября 2005 г.). Разработка ядра Linux . Pearson Education. ISBN 978-81-7758-910-8.
- ДЕХУРИ, САТЧИДАНАНДА; ДЖАГАДЕВ, АЛОК КУМАР; РАТ, АМИЯ КУМАР (8 мая 2007 г.). ОБЪЕКТИВНОЕ ПРОГРАММИРОВАНИЕ С ИСПОЛЬЗОВАНИЕМ C ++ . PHI Learning Pvt. ООО ISBN 978-81-203-3085-6.
Внешние ссылки
- Встроенные функции с коллекцией компиляторов GNU (GCC)