C++ Программирование в среде С++ Builder 5

Расширяемые функции


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

По своем завершении функция возвращает управление инструкцией RET. Выполнение вызывающей процедуры возобновляется с инструкции, следующей за call.

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

Вот простейший пример расширяемой функции:

#include <stdio.h>

inline int. Max(int a, int b)

{

return a > b? a : b;

}

int main(void)

int x = 11, у = 22;

printf("Max(%d, %d) = %d.\n", x, у, Мах(х, у)) ;

return 0;



}

Если зы хотите, чтобы компилятор генерировал расширения iniine-функций, нужно сбросить флажок Disable inline expansions на странице Compiler диалога Project Options.

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

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


Привлекая самые общие соображения, можно сформулировать следующее правило: для того, чтобы вызов iniine-функций замещался ее расширением, компилятор должен видеть определение функции в точке ее вызова. Если он ее не видит, то рассматривает функцию как внешнюю, т. е. определенную ниже или вообще в другом файле. Современные компиляторы по большей части однопроходные. Термин не имеет отношения к животному миру Австралии, а означает, что компилятор сканирует текст транслируемого модуля единственный раз и не может “заглянуть вперед”.



Компилятор Borland C++ не может расширять встроенные функции также в следующих случаях:

  • Если функция содержит операторы цикла или оператор выбора switch.


  • Если функция имеет тип не void и не содержит при этом оператора return.


  • Если она содержит встроенный код ассемблера.


  • В качестве иллюстрации рассмотрите такой пример:

    /////////////////////////////////////////////////

    // Inline.срр: Расширение inline-функций.

    //

    #pragma hdrstop

    #include <condefs.h>

    #include <stdio.h>

    inline int Max(int, int);

    int МахЗ(int, int, int);

    int main(void) {

    int x = 11, у = 22, z = 33, v = 44;

    x = МахЗ(х, z, v) ;

    z = Мах(х, у);

    // Не расширяется - генерируется вызов!

    printf("Max(%d, %d) = %d.\n", х, у, z);

    return 0;

    }

    inline int Max(int a, int b) {

    return a > b? a : b;

    }

    int МахЗ (int a, int b, int c)

    {

    b = Max(a, b);

    // Эти вызовы расширяются как встроенные.

    return Max(с, b) ;

    }

    Здесь функция Мах () определяется после main (), поэтому ее вызов из main () не расширяется как встроенный. Однако после определения Мах () дальнейшие ее вызовы (в Мах3 ()) уже расширяются. Воспользуйтесь отладчиком, чтобы это проверить — внутрь встроенной функции нельзя войти командой Trace Into; ее можно отлаживать только в окне CPU.



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


    Содержание раздела