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

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

В языке программирования 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.

Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850