Основы программирования на С++ для начинающих

Встроенные функции (inline-функции)

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

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

Итак, что из себя представляет обычная функция? Возьмем например простой пример – вычисление факториала.

Достаточно простые вычисления факториала (5!) в цикле for, и возврат результата из функции. С++ расценивает эту функцию как некий блок операций, сгруппированный в отдельный блок. Блок этот после компиляции помещается в ячейки памяти единожды в коде, и тело функции (цикл в данном случае) нигде больше в скомпилированной программе не повторяется. Все красиво – получается некий участок памяти, принадлежавший программе, на который процессор при необходимости перескакивает с того места, где находит вызов.

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

Inline функция избавляет процессор прыгать в ячейку, по адресу которой начинается эта функция. Сам смысл inline состоит в том, чтобы вместо вызова функции подставить ее тело (код функции) в место, где она вызывается.

Если описать наш факториал так:

То вместо

мы получим разворот функции в набор операторов:

как будто бы сами в коде написали в этом месте.

Соответственно код типа:

превратится в

Если мерить философски – количество кода с inline функцией увеличилось. Вместо одной строки вызова функции ее тело, подставленное вместо вызова , дало целых 6 строк. Так же будет и в скомпилированной программе – количество операторов возрастет многократно – на столько, сколько операторов в теле функции и сколько раз ее вписали в программу.

Т.е. различие между inline функцией и обычной функцией – дублирование кода тела функции везде, где она оказывается задействована. В обычной функции, её тело находится в единственном экземпляре в одном и том же месте внутри программы.

Где здесь выгода спросите вы? Экономится время процессора на прыжки с места вызова в тело функции. Если функция огромная и используется в нескольких местах, то inline получается не совсем выгодно. Однако, если функция (тело её) маленькое, с минимальным количеством операторов, решающих задачу, в старину было удобнее отказаться от прыжка и просто подставить их в нужное место, как будто сам программист там их описал.

Как бы там ни было, в бытовых условиях да еще и на современных компьютерах, программы, использующие inline подстановку тела функции вместо вызова, не дают особых преимуществ. Поэтому использовать этот вид функции приходится достаточно редко. ИМХО, ему место в музее славы. Хотя, чтоб быть до конца честным, такой подход дает свои плоды тем, кто программирует контроллеры, процессоры и прочие железяки. Но там свои особенности и свой подход, и в это углубляться сейчас не стоит. Использовать встроенную функцию (inline функцию) или нет – решать самому программисту. От себя могу добавить только одно – не стоит делать это там, где этого не требуется по заданию.

16 thoughts on “Встроенные функции (inline-функции)

  1. > Поэтому использовать этот вид функции приходится достаточно редко.

    Использовать inline функции явно приходится использовать, возможно, и не так часто. Но они массово используются неявно при определении классов и создании объектов:
    – любая функция-метод, определение котрой находится внутри определения её класса, неявно получает квалификатор inline.

    На примере класса, определяющего 2D-точку (на плоскости):
    class point {
    private:
    float x, y;
    public:
    ...
    float operator -( const point& p ) { // расстояние 2-х 2D
    float dx = ( x - p.x ), dy = ( y - p.y );
    return sqrtf( dx * dx + dy * dy );
    }
    };

    И альтернативное 2-е определение, очень похожее:
    class point {
    private:
    float x, y;
    public:
    ...
    float operator -( const point& p ); // расстояние 2-х 2D
    };

    float point::operator -( const point& p ) {
    float dx = ( x - p.x ), dy = ( y - p.y );
    return sqrtf( dx * dx + dy * dy );
    };

    В 1-м варианте расстояние между 2-мя точками будет везде вычисляться inline, а во втором – вызовом функции-метода.

    1. Это уже забота компилятора. Имеется ввиду самому программисту редко приходится в бытовых условиях использовать этот вид функций.

      1. Нет. Это как раз тот случай, когда компилятору запрещено вмешиваться в реализацию метода: если метод описан внутри класса – inline, а если внутри метод только объявляе, а реализация либо вне объявления класса или в отдельном файле (что чаще) – функцимональный вызов.
        Это как-раз случай, когда реализация находится полностью под контролем автора, а автор должен знать чем его определения обернутся.

      2. Принцип инлайнов не меняется в зависимости от того, в ООП его применяют или в функционалке )
        К чему такие усложнения теории? Я не против уточнений, но тогда нужна еще одна статья, описывающая разницу между тем и этим случаями. Нет никакой надобности мешать всю теорию в кучу – получится тот же самый MSDN.

  2. ещё одной “мелочью” является то, что квалификатор inline является рекомендательным: компилятор использует его только если это возможно, если он считает, что это недопустимо, он использует функциональный вызов без всякого на то укедомленя (даже предупреждением).

    Пример – определите функцию факториала так:
    inline unsigned long long fact3( int n ) {
    return 1 == n ? 1 : n * fact3( n - 1 );
    }

    Здесь inline не сыграет никакой роли.

    1. Ну то же самое. В современном Си компилятор сам решает. В Си для 8086 или 80286 не все компиляторы так оптимизировали.

  3. Ещё одна “мелочь”: функции с квалификатором inline замечательно работают, но до тех пор, пока определение функции и вызов находятся в одном файле кода. При росте проекта, стоит разделить код на несколько файлов (раздельная компиляия с последующим связыванием объектных файлов), и это создаст огромные проблемы с поиском ошибки: inline функци не пригодны для внешнего связывания.

    inline – это определитель макроопределения, и они непригодны для внешнего связывания (линкования).

  4. > Этот тип функций вообще-то использовался не только в Си, и в то лихое время оправдывал себя.

    В стандарте языка C вообще нет ключевого слова inline, там подобные вещи реализуются через параметризированные макросы #define.
    В некоторых поздних компиляторах C inline включено, но только для пддержания совместимсоти с C++.
    Поэтому в принципе неверно говорить, что inline заимствовано из C, это – чисто C++ приобретение.
    Поэтому я бы вот то “залихватское” начало выбросил.

    1. А я не намекал на стандарт. Я намекал на ЯВУ. В Паскале тоже такие функции существовали. Да и в бейсике кажется было что-то подобное в поздних его видах.

      1. > Этот тип функций вообще-то использовался не только в Си

        В языке C не было ключевого слова inline от момента, когда его в 1969г. начал формулировать Денис Ритчи, и почти до 2000-х годов.
        Всё это время если слов inline появилось программе C, оно бы вызвало синтаксическую ошибку.

  5. К вопросу совместимости C и C++ … в отношении inline:

    Если компилировать код как C, но с ключом -std=c89 (т.е. стандарт ANSI 1989г.), то на inline просто будет синтаксическая ошибка. И на всех более ранних стандартах языка C.
    Использование inline в C допустит только компилятор с опцией -std=c99, т.е. такое ключевое слово допускается стандартом C только после 1999г.

  6. Вообще то, статья про inline функции, в отличие от большинства статей сайта – неудачная. Мне кажется, что её следовало бы слегка скорректировать, добавив и исправив с учётом указанных позиций.

    1. Я, когда ее писал, не желал вдаваться в особые подробности, чтоб не загораживать ими саму суть. Не зря в интернете есть правило “Многабукаф”, которое зачастую раздувает контент в салат Ой-Ливьйо.

  7. inline также не будет работать с рекурсивными функциями и т.п. У inline много нюансов .
    я делаю встроенными только критичные одна строчные функции

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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *