Указатели на функции

Последнее обновление: 24.03.2023

Указатель на функцию (function pointer) хранит адрес функции. По сути указатель на функцию содержит адрес первого байта в памяти, по которому располагается выполняемый код функции.

Самым распространенным указателем на функцию является ее имя. С помощью имени функции можно вызывать ее и получать результат ее работы.

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

тип (*имя_указателя) (типы_параметров);
  • тип представляет тип возвращаемого функцией значения.

  • имя_указателя представляет произвольно выбранный идентификатор в соответствии с правилами о наименовании переменных.

  • параметры определяют типы параметров через запятую (при их наличии).

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

Например, определим указатель на функцию:

void (*message) ();

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

Используем указатель на функцию:

#include <iostream>

void hello();
void goodbye();
 
int main()
{
    void (*message)();  // определение указателя на функцию
     
    message=hello;
    message();
    message = goodbye;
    message();
}
void hello()
{
    std::cout << "Hello, World" << std::endl;
}
void goodbye()
{
    std::cout << "Good Bye, World" << std::endl;
}

Указателю на функцию можно присвоить функцию, которая соответствует указателю по возвращаемому типу и спецификации параметров:

message=hello;

То есть в данном случае указатель message теперь хранит адрес функции hello. И посредством обращения к указателю мы можем вызвать эту функцию:

message();

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

(*message)();

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

Hello, World
Good Bye, World

При определении указателя стоит обратить внимание на скобки вокруг имени. Так, использованное выше определение

void (*message) ();

НЕ будет аналогично следующему определению:

void *message ();

Во втором случае определен не указатель на функцию, а прототип функции message, которая возвращает указатель типа void*.

Определение и инциализация указателя

При определении указатель можно сразу инициализировать:

void (*message)() {hello}; // указывает на функцию hello
// или так
void (*message2)() =hello; // указывает на функцию hello

Можно инициализировать значением nullptr:

void (*message)() { nullptr};

Если указатель при определении инициализируется какой-либо функцией, то можно опустить все определение типа и просто использовать слово auto:

auto message { hello}; // указывает на функцию hello
auto message2 = hello; // указывает на функцию hello

Можно подчеркнуть, что переменная является именно указателем, указав после auto символ звездочки:

auto* message { hello};

Но особой разницы - что со звездочкой, что без звездочки нет.

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

auto message { &hello};

Но в принципе применение такого символа, как и символа звездочки с auto, ни на что не влияет.

Указатель на функцию с параметрами

Рассмотрим еще один указатель на функцию:

#include <iostream>
 
int sum(int, int);
int subtract(int, int);
  
int main()
{
    int a{10};
    int b{5};
    int (*operation)(int, int) {sum};	// указатель operation указывает на функцию sum
    int result = operation(a, b);
    // result = (*operation)(a, b); // альтернативный вариант
    std::cout << "result = " << result << std::endl;     // result = 15
      
    operation = subtract;	// указатель operation указывает на функцию subtract
    result = operation(a, b);
    std::cout << "result = " << result << std::endl;     // result = 5
}
int sum(int x, int y)
{
    return x+y;
}
int subtract(int x, int y)
{
    return x-y;
}

Здесь определен указатель operation, который может указывать на функцию с двумя параметрами типа int, возвращающую также значение типа int. Соответственно мы можем присвоить указателю адреса функций add и subtract и вызвать их, передав при вызове указателю некоторые значения для параметров.

Массивы указателей на функции

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

тип (*имя_массива[размер]) (параметры)

Например:

double (*actions[]) (int, int)

Здесь actions представляет массив указателей на функции, каждая из которых обязательно должна принимать два параметра типа int и возвращать значение типа double.

Посмотрим применение массива указателей на функции на примере:

#include <iostream>
 
void add(int, int);
void subtract(int, int);
void multiply(int, int);
 
int main()
{
    int a {10};
    int b {5};
    void (*operations[3])(int, int) = {add, subtract, multiply};
      
    // получаем длину массива
    unsigned length = std::size(operations);
      
    for(unsigned i{}; i < length; i++)
    {
        operations[i](a, b);    // вызов функции по указателю
    }
}
void add(int x, int y)
{
    std::cout << "x + y = " << x + y << std::endl;
}
void subtract(int x, int y)
{
    std::cout << "x - y = " << x - y << std::endl;
}
void multiply(int x, int y)
{
    std::cout << "x * y = " << x * y << std::endl;
}

Здесь массив operations содержит три функции add, subtract и multiply, которые последовательно вызываются в цикле через перебор массива в функции main.

Консольный вывод программы:

x + y = 15
x - y = 5
x * y = 50
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850