Встроенные функции C++ (понять за секунды)

встроенная функция

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

Процесс выполнения программы на C/C++ можно рассматривать как процесс взаимных вызовов между несколькими функциями, образующими простую или сложную цепочку вызовов, начальной точкой этой цепочки является main(), а конечной точкой также является main() . Когда функция main() завершает вызов всех функций, она возвращает значение (например return 0;) для завершения своей жизни, тем самым завершая всю программу.

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

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

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

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

#include <iostream>
using namespace std;

//内联函数,交换两个数的值
inline void swap(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

int main()
{
    int m, n;
    cin>>m>>n;
    cout<<m<<", "<<n<<endl;
    swap(&m, &n);
    cout<<m<<", "<<n<<endl;

    return 0;
}

//运行结果:
//45 99
//45, 99
//99, 45

Обратите внимание, что необходимо добавить ключевое слово inline в определение функции.Хотя нет ничего плохого в добавлении ключевого слова inline в объявление функции, этот подход недопустим, и компилятор проигнорирует ключевое слово inline в объявлении функции.

Когда компилятор встречает вызов функции swap(&m, &n), он заменяет его кодом функции swap() swap(&m, &n), а формальный параметр заменяет фактическим параметром Результат после замены следующий:

int temp;
temp = *(&m);
*(&m) = *(&n);
*(&n) = temp;

Компилятор может оптимизировать *(&m), *(&n) до m, n соответственно.

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

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

#include <iostream>
using namespace std;

//声明内联函数
void swap1(int *a, int *b); //也可以添加inline,但编译器会忽略

int main()
{
    int m, n;
    cin>>m>>n;
    cout<<m<<", "<<n<<endl;
    swap1(&m, &n);
    cout<<m<<", "<<n<<endl;

    return 0;
}

//定义内联函数
inline void swap1(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

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

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

Встроенные функции и определения макросов

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

#define MAX(a,b) (a)>(b)?(a):(b)

заявление:

result = MAX(i,j)+2;
//被预处理器扩展为 
result = (i)>(j)?(i):(j)+2;

Поскольку оператор «+» имеет более высокий приоритет, чем оператор «?:», приведенное выше утверждение не эквивалентно

result = ((i)>(j)?(i):(j))+2;

Если код макроса переписать как:

#define MAX(a,b) ((a)>(b)?(a):(b))

Это может решить проблему приоритета. но возникает другая проблема

result = MAX(i++,j);
//被预处理器扩展为 
result = (i++)>(j)?(i++):(j) //在同一个表达式中i被两次求值。

Еще одним недостатком макросов является то, что они не поддаются отладке.

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

Когда использовать встроенные функции

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

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

Следующие ситуации не должны использоваться встроенно:

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

Guess you like

Origin blog.csdn.net/wzz953200463/article/details/116303725#comments_25988701
C++