В языке программирования C функция тоже имеет адрес и может иметь указатель. Указатель на функцию представляет собой выражение или переменную, которые используются для представления адреса функции. Указатель на функцию содержит адрес первого байта в памяти, по которому располагается выполняемый код функции.
Самым распространенным указателем на функцию является ее имя. С помощью имени функции мы можем вызывать ее и получать результат ее работы.
Но также указатель на функцию можно определять в виде отдельной переменной с помощью следующего синтаксиса:
тип (*имя_указателя) (типы_параметров);
Здесь тип представляет тип возвращаемого функцией значения.
имя_указателя представляет произвольно выбранный идентификатор в соответствии с правилами о наименовании переменных.
После названия указателя в скобках идут через запятую типы параметров. Если функция не принимает параметров, то указывается void
.
Например, определим указатель на функцию:
void (*message) (void);
Здесь определен указатель, который имеет имя message. Он может указывать на функции без параметров, которые возвращают
тип void
(то есть ничего не возвращают). После названия указателя идет (void)
, что указывает, что функция не принимает параметров.
Применим этот указатель на функцию:
#include <stdio.h> void hello() { printf("Hello, World \n"); } void goodbye() { printf("Good Bye, World \n"); } int main(void) { // определяем указатель на функцию void (*message) (void); message=hello; // указатель указывает на функцию hello message(); // вызываем функцию, на которую указыывет указатель message = goodbye; // указатель указывает на функцию goodbye message(); // вызываем функцию, на которую указыывет указатель return 0; }
Указателю на функцию можно присвоить функцию, которая соответствует указателю по возвращаемому типу и спецификации параметров:
message=hello;
То есть в данном случае указатель message теперь хранит адрес функции hello. И посредством обращения к указателю мы можем вызвать эту функцию:
message();
Впоследствии мы можем присвоит указателю адрес другой функции, как в данном случае. В итоге результатом данной программы будет следующий вывод:
Hello, World Good Bye, World
При определении указателя стоит обратить внимание на скобки вокруг имени. Так, использованное выше определение
void (*message) (void);
НЕ будет аналогично следующему определению:
void *message (void);
Во втором случае определен не указатель на функцию, а прототип функции message, которая возвращает указатель типа void*.
Следует учитывать, что указатель на функцию должен по типу возвращаемого значения и типу параметров соответствовать функции, иначе он не сможет соответствовать этой функции.
Рассмотрим еще один указатель на функцию, которая возвращает значение типа int
и принимает два параметра типа int
:
#include <stdio.h> int sum(int x, int y) { return x + y; } int subtract(int x, int y) { return x - y; } int main(void) { int a = 10; int b = 5; int result; int (*operation)(int, int); operation=sum; result = operation(a, b); printf("result = %d \n", result); // result=15 operation = subtract; result = operation(a, b); printf("result = %d \n", result); // result=5 return 0; }
Здесь определен указатель operation, который может указывать на функцию с двумя параметрами типа int, возвращающую также значение типа int. Соответственно мы можем присвоить указателю адреса функций sum и subtract и вызвать их, передав при вызове в указатель нужные значения для параметров.
Кроме одиночных указателей на функции мы можем определять их массивы. Для этого используется следующий формальный синтаксис:
тип (*имя_массива[размер]) (параметры)
Например:
double (*actions[]) (int, int)
Здесь actions представляет массив указателей на функции, каждая из которых обязательно должна принимать два параметра типа int и возвращать значение типа double.
Посмотрим применение массива указателей на функции на примере:
#include <stdio.h> void sum(int x, int y) { printf("x + y = %d \n", x + y); } void subtract(int x, int y) { printf("x - y = %d \n", x - y); } void multiply(int x, int y) { printf("x * y = %d \n", x * y); } int main(void) { int a = 10; int b = 5; void (*operations[3])(int, int) = {sum, subtract, multiply}; // получаем длину массива int length = sizeof(operations)/sizeof(operations[0]); for(int i=0; i < length; i++) { operations[i](a, b); // вызов функции по указателю } return 0; }
Здесь массив operations содержит три функции sum, subtract и multiply, которые последовательно вызываются в цикле через перебор массива в функции main.