Указатель на функцию как создать
Перейти к содержимому

Указатель на функцию как создать

  • автор:

Указатель на функцию как создать

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

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

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

тип (*имя_указателя) (типы_параметров);

Здесь тип представляет тип возвращаемого функцией значения.

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

После названия указателя в скобках идут через запятую типы параметров. Если функция не принимает параметров, то указывается void .

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

void (*message) (void);

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

Применим этот указатель на функцию:

#include 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 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 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) = ; // получаем длину массива 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.

Указатель на функцию как создать

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

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

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

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

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

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

void (*message) ();

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

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

#include void hello(); void goodbye(); int main() < void (*message)(); // определение указателя на функцию message=hello; message(); message = goodbye; message(); >void hello() < std::cout void goodbye()

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

message=hello;

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

message();

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

(*message)();

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

Hello, World Good Bye, World

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

void (*message) ();

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

void *message ();

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

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

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

void (*message)() ; // указывает на функцию 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 int sum(int, int); int subtract(int, int); int main() < int a; int b; int (*operation)(int, int) ; // указатель operation указывает на функцию sum int result = operation(a, b); // result = (*operation)(a, b); // альтернативный вариант std::cout int, возвращающую также значение типа int. Соответственно мы можем присвоить указателю адреса функций add и subtract и вызвать их, передав при вызове указателю некоторые значения для параметров.

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

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

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

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

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

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

Документация

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

Что такое указатель на функцию?

Указателем на функцию является MATLAB ® тип данных, который хранит ассоциацию к функции. Косвенно вызывание функции позволяет вам вызвать функцию независимо от того, откуда вы вызываете его. Типичное использование указателей на функцию включает:

  • Передача функции к другой функции (часто названный функциональными функциями ). Например, передавая функцию интегрированию и оптимизационным функциям, таким как integral и fzero .
  • Задавая функции обратного вызова (например, коллбэк, который отвечает на событие UI или взаимодействует с оборудованием сбора данных).
  • Построение указателей на функции, определяемые встраивает вместо сохраненного в программном файле (анонимные функции).
  • Вызывание локальных функций снаружи основной функции.

Вы видите если переменная, h , использование указателя на функцию isa (h,'function_handle') .

Создание Указателей на функции

Чтобы создать указатель для функции, предшествуйте имени функции с @ знак. Например, если у вас есть функция под названием myfunction , создайте указатель под названием f можно следующим образом:

f = @myfunction;

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

function y = computeSquare(x) y = x.^2; end

Создайте указатель и вызовите функцию, чтобы вычислить квадрат четыре.

f = @computeSquare; a = 4; b = f(a)
b = 16

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

h = @ones; a = h()
a = 1

Без круглых скобок присвоение создает другой указатель на функцию.

a = h
a = @ones

Указатели на функцию являются переменными, которые можно передать другим функциям. Например, вычислите интеграл x 2 на области значений [0,1].

q = integral(f,0,1);

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

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

  • Назовите длину — Каждая часть имени функции (включая пакет и имена классов) должна быть меньше номера, заданного namelengthmax . В противном случае MATLAB обрезает последнюю часть имени.
  • Осциллограф Функция должна быть в осциллографе в то время, когда вы создаете указатель. Поэтому функция должна быть на пути MATLAB или в текущей папке. Или для указателей на локальные или вложенные функции функция должна быть в текущем файле.
  • Приоритет — Когда существует несколько функций с тем же именем, MATLAB, использует те же правила приоритета, чтобы задать указатели на функцию, как это делает, чтобы вызвать функции. Для получения дополнительной информации смотрите Порядок приоритета функций.
  • Перегружаясь — Когда указатель на функцию вызывается с одним или несколькими аргументами, MATLAB определяет доминирующий аргумент. Если доминирующий аргумент является объектом, MATLAB определяет, имеет ли класс объекта метод, который перегружает то же имя как присоединенная функция указателя на функцию. Если это делает, то метод объекта вызывается вместо присоединенной функции.

Анонимные функции

Можно создать указатели на анонимные функции. Анонимной функцией является короткая основанная на выражении функция MATLAB, которая не требует программного файла. Создайте указатель на анонимную функцию путем определения тела функции, anonymous_function , и список, разделенный запятыми входных параметров к анонимной функции, arglist . Синтаксис:

h = @(arglist)anonymous_function

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

sqr = @(n) n.^2; x = sqr(3)

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

Чаще всего к ошибкам приводит использование указателей па функцию. Хотя функция - это не переменная, она по-прежнему имеет физическое положение в памяти, которое может быть присвоено указателю. Адрес, присвоенный указателю, является входной точкой в функцию. Указатель может использоваться вместо имени функции. Он также позволяет передавать функции как обычные аргументы в другие функции.

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

Адрес функции получается при использовании имени функции без каких-либо скобок или аргументов. (Очень похоже на массивы, где адрес получается с использованием имени массива без индексов.) Рассмотрим следующую программу, предназначенную для демонстрации нюансов объявления:

#include
#include
void check(char *a, char *b, int (*cmp) (const char *, const char *));
int main(void)
char s1 [80], s2[80];
int (*p) (const char*, const char*);
p = strcmp; /* получение адреса strcmp() */
gets(s1);
gets (s2);
check(s1, s2, p);
return 0;
>

void check (char *a, char *b, int (*cmp) (const char *, const char *))
printf("Testing for equality.\n");
if(!(*cmp) (a, b)) printf("Equal");
else printf("Not equal");
>

Когда вызывается check(), ей передаются два указателя на символы и один указатель на функцию. В функции check() аргументы объявляются как указатели на символы и указатель на функцию. Надо обратить внимание на способ объявления указателя на функцию. Следует использовать аналогичный метод при объявлении других указателей на функцию, за исключением тех случаев, когда отличается возвращаемый тип или передаваемые параметры. Скобки вокруг *cmp необходимы для правильной интерпретации компилятором данного выражения.

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

Рассмотрим работу функции strcmp() в функции check(). Оператор

if (!(*cmp) (a, b) ) printf("Equal");

осуществляет вызов функции, в данном случае strcmp(), с помощью cmp, который указывает на данную функцию. Вызов происходит с аргументами a и b. Данный оператор демонстрирует общий вид использования указателя на функцию для вызова функции, на которую он указывает. Круглые скобки вокруг *cmp необходимы вследствие наличия приоритетов. На самом деле можно напрямую использовать cmp, как показано ниже:

if (!cmp(a, b)) printf("Equal");

Данная версия также вызывает функцию, на которую указывает cmp, но она использует нормальный синтаксис. Использование ( cmp) помогает всем, читающим программу, понять, что указатель на функцию используется для вызова функции вместо вызова функции cmp. Возможно вызвать напрямую check(), используя strcmp, как показано ниже:

check(s1, s2, strcmp);

Данный оператор устраняет необходимость наличия дополнительной переменной-указателя.

Можно задаться вопросом, почему кто-то хочет написать программу таким способом. В данном примере ничего не достигается, но появляются большие проблемы. Тем не менее иногда бывают моменты, когда выгодно передавать функции в процедуры или хранить массивы функций. Следующий пример демонстрирует использование указателей на функции. При написании интерпретатора наиболее типично использование вызовов функций для различных подпрограмм поддержки, типа синус, косинус и тангенс. Вместо использования большого списка для оператора switch, можно использовать массив указателей на функции, в котором доступ к необходимой функции осуществляется с помощью индекса. В данной программе check() может использоваться для проверки как алфавитного, так и численного равенства простым вызовом различных функций сравнения.

#include
#include
#include
#include
void check(char *a, char *b, int (*cmp) (const char *, const char *));
int numcmp (const char *a, const char *b) ;
int main(void)
char s1[80], s2 [80];
gets (s1);
gets (s2);
if(isalpha(*s1))
check (s1, s2, strcmp);
else
check(s1, s2, numcmp);
return 0;
>

void check(char *a, char *b, int (*cmp) (const char *,const char *))
printf("Testing for equality.\n");
if(!(*cmp) (a, b)) printf ("Equal");
else printf("Hot equal");
>

int numcmp (const char *a, const char *b)
If(atoi(a)==atoi(b)) return 0;
else return 1;
>

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *