«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Встроенные функции в C и C++

Встроенные функции в C и C++

Опубликовано 8 ноября 2024 г.
Просматривать:305

Inline Functions in C and C

Введение

C добавлено встроенное ключевое слово, которое может префикс определения функции, например:

inline int max_int( int a, int b ) {
    return a > b ? a : b;
}

, чтобы дать компилятору «подсказку», что программа в целом может повысить производительность за счет встроенной функции.

Код функции, которая была встроена, расширялась в каждой точке ее вызова вместо выполнения обычного механизма вызова функции:

  • Сохранение регистров.
  • Помещение значений аргументов в стек.
  • Выполнение инструкции вызова.
  • Функция, в конечном итоге выполняющая инструкцию ret.
  • Восстановление регистров.

Для очень маленьких функций встраивание может дать прирост производительности. Но, как и во всем остальном, здесь есть компромиссы.

Ключевое слово inline было перенесено на C99, но с немного другими требованиями — об этом позже.

Отличия от макросов

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

Макрос, эквивалентный функции max_int():

#define MAX_INT(A,B)  A > B ? A : B  /* bad implementation */

имеет следующие проблемы:

  • Расширенные аргументы, например MAX(n & 0xFF, 8), могут привести к неправильному приоритету операторов.
  • Аргументы, имеющие побочные эффекты, например MAX(n , 8), могут иметь несколько побочных эффектов.
  • Проверка типов аргументов при определении не осуществляется.
  • Ошибки часто бывают многословными и их трудно читать.

Дополнительно макрос:

  • Можно изменить свой аргумент (часто это не то, что вы хотите).

Встроенные функции не имеют ни одной из этих проблем, но могут обеспечить такой же выигрыш в производительности. Следовательно, используйте встроенные функции вместо функциональных макросов.

Только подсказка

Как уже упоминалось, указание встроенной функции — это только «подсказка» компилятору о том, что программа в целом может выиграть в производительности от встроенной функции. Компилятор может игнорировать подсказку.

Почему? Потому что бывают случаи, когда это либо не очень хорошая идея, либо невозможно. Функция либо не является встроенной, либо обычно не является встроенной, если выполняется одно из следующих условий:

  • Функция «слишком большая».
  • Вы вызываете функцию через указатель на функцию.
  • Функция рекурсивная.
  • Функция имеет цикл.

Могут быть и другие причины. Все это сильно зависит от функции, ее аргументов, компилятора и любых предоставленных ей опций.

Если компилятор либо не может, либо предпочитает не встраивать функцию, он не предупреждает вас, что он этого не сделал (по умолчанию). Некоторые компиляторы, например, gcc, имеют опцию -Winline, которая предупредит вас и сообщит причину, по которой функция не была встроена.

Указание inline аналогично указанию регистра в старом коде — оба они являются лишь подсказками.

Когда (а когда нет) использовать Inline

Для большинства функций основная часть затрат на выполнение функции приходится на тело функции, а не на механизм вызова функции. Следовательно, чтобы функция была хорошим кандидатом на встраивание, она обычно должна быть:

  • Достаточно мал, чтобы доминировать над стоимостью механизма вызова функций.
  • Используется там, где производительность действительно имеет значение, например, в узких циклах.

Если сомневаетесь, профилируйте свой код. Использование inline — это не волшебное ключевое слово «сделай меня быстрее». Кроме того, чрезмерное использование встроенных функций может привести к раздуванию кода, что дополнительно приведет к ухудшению производительности вашей программы в целом.

Подробнее см. в разделе «Встроенная болезнь».

К функциям, которые часто являются хорошими кандидатами на встраивание, относятся:

  • Острые фразы, такие как «геттеры» и «сеттеры».
  • Простые оболочки вокруг вызовов других функций, которые передают определенные значения аргументам или выполняют приведение типов.

Идеальная встроенная функция. увеличивает производительность, и уменьшает размер кода.

Однако есть одно предостережение относительно любой встроенной функции: если ее определение изменится, потребуется перекомпиляция всего кода, который ее использует.

Встроенные оптимизации

Если встроенная функция фактически встроена компилятором, то, помимо исключения кода обычного механизма вызова функций, компилятор также может:

  • Полностью исключить один или несколько аргументов функции, значения которых являются константами, посредством непосредственной адресации.
  • Выполняйте более качественную оптимизацию кода, в который встроена функция, и который обычно не может выполняться за пределами функции.

Определение встроенной функции

Чтобы компилятор мог встроить функцию, он должен иметь возможность «видеть» ее определение (а не только ее объявление) в каждом файле .c или .cpp, в котором он используется. точно так же, как макрос. Следовательно, встроенная функция должна быть определена в заголовочном файле.

Обычно функция, как и все остальное, должна иметь ровно одно определение, придерживаясь правила одного определения (ODR). Однако, поскольку определение встроенной функции «видно» в нескольких файлах .c или .cpp, ODR для этой функции приостанавливается.

Можно иметь разные определения для встроенных функций с одинаковым именем, но это приводит к неопределенному поведению, поскольку у компилятора нет возможности проверить, что все определения одинаковы.

Чтобы встроить функцию в C, все, что вам нужно сделать, это поставить перед определением функции префикс inline — вот и все. Компилятор и/или компоновщик автоматически удалит все определения из конечного исполняемого файла, кроме одного.

Однако, чтобы встроить функцию в C, вы дополнительно должны явно указать компилятору, в какой файл .o следует поместить одно определение, на случай, если компилятор либо не сможет, либо не захочет встроить функцию через внешний встроенный.

Например, в ровно одном .c файле вы должны объявить такую ​​функцию:

// util.c
extern inline int max_int( int, int );

Это говорит компилятору «поместить одно определение для max_int() в util.o».

В качестве альтернативы в C вы также можете объявить встроенную функцию статической:

static inline int max_int( int a, int b ) {
    return a > b ? a : b;
}

Если вы сделаете это, то:

  • Вам не нужно где-либо объявлять внешнюю встроенную функцию.
  • Однако, если компилятор не встраивает функцию, он будет генерировать определение в каждом файле .c, в который она включена, что снова приведет к раздуванию кода.
  • Если функция имеет какие-либо статические локальные переменные, каждое определение будет иметь отдельные копии (которые могут быть, а могут и не быть тем, что вам нужно).

Заключение

Встроенные функции при разумном использовании могут повысить производительность. Как правило, только очень маленькие функции являются хорошими кандидатами для встраивания.

Начиная с C 11, встроенные функции можно альтернативно объявлять constexpr, но это уже история в другой раз.

Ссылки

  • Стиль кодирования ядра Linux, §15 «Встроенная болезнь».
  • Миф и реальность об инлайне в C99.
  • Справочное руководство по C с аннотациями, Маргарет А. Эллис и Бьерн Страуструп, Addison-Wesley, 1990, ISBN 0-201-51459-1, §7.1.2 Спецификаторы функций, стр. 99–105.
Заявление о выпуске Эта статья воспроизведена по адресу: https://dev.to/pauljlucas/inline-functions-in-c-and-c-2040. Если есть какие-либо нарушения, свяжитесь с [email protected], чтобы удалить их.
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3