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

Как передать указатель в функцию c

  • автор:

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

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

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

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

#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;
>

Передача указателей в функцию C++

Вопрос по C++. При передаче обычной переменной в функцию создается, как я понимаю, её копия. А что происходит, когда мы передаем указатель? Создается ли копия указателя или нет?

Отслеживать
44.8k 3 3 золотых знака 39 39 серебряных знаков 90 90 бронзовых знаков
задан 24 дек 2016 в 23:45
113 1 1 золотой знак 1 1 серебряный знак 5 5 бронзовых знаков

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

Если у вас объявлена функция с параметром, как, например,

void f( T t ); 

где T — это некоторый тип, и эта функция вызывается с некоторым выражением, переданным ей в качестве аргумента, как

f( exp ); 

то инициализацию параметра можно представить следующим образом

void f( /* T t */ ) < T t = exp; //. 

то есть параметр - это локальная переменная функции, которая инициализируется тем выражением, которое передано функции в качестве аргумента. Следовательно изменения параметра никак не сказываются на исходном аргументе, ели только тип T не является ссылочным типом.

Сравните две функции

void f( int *p ) < p = new int( 10 ); >void g( int * &p )
int main()

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

У функции g параметр имеет ссылочный тип, то есть эта ссылка на переданный функции аргумент. Поэтому функция имеет дело с исходным аргументом и меняет его в своем теле, присваивая ему адрес выделенной памяти.

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

void h( int **p )

Вызов функции будет выглядеть как

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

К ак уже обсуждалось ранее функции – это набор команд, которые расположены в соответствующей области памяти. Вызов функции – это сохранение состояния, передача аргументов и переход по адресу, где располагается функция. В си есть возможность создавать указатели на функции. Указатели на функции позволяют упростить решение многих задач. Совместно с void указателями можно создавать функции общего назначения (например, сортировки и поиска). Указатели позволяют создавать функции высших порядков (функции, принимающие в качестве аргументов функции): отображение, свёртки, фильтры и пр. Указатели на функции позволяют уменьшать цикломатическую сложность программ, создавать легко масштабируемые конструкции. Рассмотрим пример. Создадим функцию и указатель на эту функцию

#include #include int sum(int a, int b) < return a + b; >void main () < //Объявляем указатель на функцию int (*fptr)(int, int) = NULL; int result; //Присваиваем указателю значение - адрес функции //Это похоже на работу с массивом: операцию взятия адреса использовать не нужно fptr = sum; //Вызов осуществляется также, как и обычной функции result = fptr(10, 40); printf("%d", result); getch(); >
#include #include int dble(int a) < return 2*a; >int deleteEven(int a) < if (a % 2 == 0) < return 0; >else < return a; >> //Функция принимает массив, его размер и указатель на функцию, //которая далее применяется ко всем элементам массива void map(int *arr, unsigned size, int (*fun)(int)) < unsigned i; for (i = 0; i < size; i++) < arr[i] = fun(arr[i]); >> void main () < int a[] = ; unsigned i; map(a, 10, deleteEven); for (i = 0; i < 10; i++) < printf("%d ", a[i]); >map(a, 10, dble); printf("\n"); for (i = 0; i < 10; i++) < printf("%d ", a[i]); >getch(); >

В этом примере мы создали функцию отображения, которая применяет ко всем элементам массива функцию, которая передаётся ей в качестве аргумента. Когда мы вызываем функцию map, достаточно просто передавать имена функций (они подменяются указателями). Запишем теперь функцию map, которая получает в качестве аргумента массив типа void:

#include #include void dbleInt(void *a) < *((int*) a) *= 2; >void deleteEvenInt(void* a) < int tmp = *((int*) a); if (tmp % 2 == 0) < *((int*) a) = 0; >> void dbleDouble(void *a) < *((double*) a) *= 2.0; >void deleteEvenDouble(void* a) < int tmp = *((double*) a); if (tmp % 2 == 0) < *((double*) a) = 0; >> //Функция принимает массив, его размер, размер одного элемента и указатель на функцию, //которая далее применяется ко всем элементам массива void map(void *arr, unsigned num, size_t size, void (*fun)(void *)) < unsigned i; char *ptr = (char*) arr; for (i = 0; i < num; i++) < fun((void*) (ptr + i*size)); >> void main () < int a[] = ; double b[] = ; unsigned i; //Работаем с массивом типа int map(a, 10, sizeof(int), deleteEvenInt); for (i = 0; i < 10; i++) < printf("%d ", a[i]); >map(a, 10, sizeof(int), dbleInt); printf("\n"); for (i = 0; i < 10; i++) < printf("%d ", a[i]); >printf("\n"); //Работаем с массивом типа double map(b, 10, sizeof(double), deleteEvenDouble); for (i = 0; i < 10; i++) < printf("%.3f ", b[i]); >map(b, 10, sizeof(double), dbleDouble); printf("\n"); for (i = 0; i < 10; i++) < printf("%.3f ", b[i]); >getch(); >

Вот где нам понадобились указатели типа void. Так как map получает указатель на функцию, то все функции должны иметь одинаковые аргументы и возвращать один и тот же тип. Но аргументы функций должны быть разного типа, поэтому мы делаем их типа void. Функция map получает указатель типа void (*)(void*), поэтому ей теперь можно передавать любую из четырёх функций.
Пример другой функции: функция filter получает указатель на массив и возвращает размер нового массива, оставляя в нём только те элементы, для которых переданный предикат возвращает логическую истину (предикат – функция, которая возвращает истину или ложь). Сначала напишем для массива типа int:

#include #include #include int isOdd(int a) < return (a % 2 != 0); >int isGtThree(int a) < return a >3; > unsigned int filter(int *arr, unsigned size, int (*pred)(int), int** out) < unsigned i; unsigned j; //размер возвращаемого масива *out = (int*) malloc(sizeof(int)*size); for (i = 0, j = 0; i < size; i++) < if (pred(arr[i])) < (*out)[j] = arr[i]; j++; >> *out = (int*) realloc(*out, j*sizeof(int)); return j; > void main () < int a[] = ; int *aOdd = NULL; int *aGtThree = NULL; unsigned i; unsigned size; size = filter(a, 10, isOdd, &aOdd); for (i = 0; i < size; i++) < printf("%d ", aOdd[i]); >printf("\n"); size = filter(a, 10, isGtThree, &aGtThree); for (i = 0; i < size; i++) < printf("%d ", aGtThree[i]); >free(aOdd); free(aGtThree); getch(); >

Теперь для массива типа void

#include #include #include #include #include int isOddInt(void *a) < return (*((int*) a) % 2 != 0); >int isGtThreeInt(void* a) < return *((int*) a) >3; > int isOddDouble(void* a) < return (int)*((double*) a) / 2 != 0; >int isGtThreeDouble(void* a) < return *((double*) a) >3.0; > unsigned int filter(void *arr, unsigned num, size_t size, int (*pred)(void*), void** out) < unsigned i; unsigned j; //размер возвращаемого масива char* ptrIn = (char*) arr; char* ptrOut = NULL; *out = (void*) malloc(num*size); ptrOut = (char*) (*out); for (i = 0, j = 0; i < num; i++) < if (pred(ptrIn + i*size)) < memcpy(ptrOut + j*size, ptrIn + i*size, size); j++; >> *out = (void*) realloc(*out, j*size); return j; > void main () < int a[] = ; double b[] = ; int *aOdd = NULL; int *aGtThree = NULL; double *bOdd = NULL; double *bGtThree = NULL; unsigned i; unsigned size; size = filter(a, 10, sizeof(int), isOddInt, (void**)&aOdd); for (i = 0; i 7lt; size; i++) < printf("%d ", aOdd[i]); >printf("\n"); size = filter(a, 10, sizeof(int), isGtThreeInt, (void**)&aGtThree); for (i = 0; i < size; i++) < printf("%d ", aGtThree[i]); >printf("\n"); size = filter(b, 10, sizeof(double), isOddDouble, (void**)&bOdd); for (i = 0; i < size; i++) < printf("%.3f ", bOdd[i]); >printf("\n"); size = filter(b, 10, sizeof(double), isGtThreeDouble, (void**)&bGtThree); for (i = 0; i < size; i++) < printf("%.3f ", bGtThree[i]); >free(aOdd); free(bOdd); free(aGtThree); free(bGtThree); getch(); >

Ещё одна функция – свёртка. Она получает в качестве аргументов массив и функцию от двух аргументов. Эта функция действует следующим образом: сначала она применяется к первым двум аргументам, затем она применяется к третьему аргументу и результату предыдущего вызова, затем к четвёртому аргументу и результату предыдущего вызова и т.д. С помощью свёртки можно, например, найти сумму всех элементов массива, максимальный элемент массива, факториал числа и т.п.

#include #include int sum(int a, int b) < return a + b; >int maxx(int a, int b) < return (a >b)? a: b; > int mul(int a, int b) < return a*b; >void fold(int *arr, unsigned size, int (*fun)(int, int), int *acc) < unsigned i; *acc = fun(arr[0], arr[1]); for (i = 2; i < size; i++) < *acc = fun(*acc, arr[i]); >> void main () < int a[] = ; int result; fold(a, 10, sum, &result); printf("%d\n", result); fold(a, 10, maxx, &result); printf("%d\n", result); fold(a, 10, mul, &result); printf("%d\n", result); getch(); >

Последний пример: функция сортировки вставками для массива типа void. Так как тип массива не известен, то необходимо передавать функцию сравнения.

#include #include #include #include int cmpIntDesc(void *a, void* b) < return *((int*) a) < *((int*) b); >int cmpIntAsc(void *a, void* b) < return *((int*) a) >*((int*) b); > int cmpDoubleAsc(void *a, void* b) < return *((double*) a) < *((double*) b); >int cmpDoubleDesc(void *a, void* b) < return *((double*) a) >*((double*) b); > void insertionSort(void* arr, unsigned num, size_t size, int (*cmp)(void *a, void *b)) < unsigned i, j; char *ptr = (char*) arr; char *tmp = (char*) malloc(size); for (i = 1; i < num; i++) < if (cmp(ptr + i*size, ptr + (i-1)*size)) < j = i; while (cmp(ptr + j*size, ptr + (j-1)*size) && j >0) < memcpy(tmp, ptr + j*size, size); memcpy(ptr + j*size, ptr + (j-1)*size, size); memcpy(ptr + (j-1)*size, tmp, size); j--; >> > > void main () < int a[] = ; double b[] = ; int i; insertionSort(a, 10, sizeof(int), cmpIntAsc); for (i = 0; i < 10; i++) < printf("%d ", a[i]); >printf("\n"); insertionSort(a, 10, sizeof(int), cmpIntDesc); for (i = 0; i < 10; i++) < printf("%d ", a[i]); >printf("\n"); insertionSort(b, 10, sizeof(double), cmpDoubleAsc); for (i = 0; i < 10; i++) < printf("%.3f ", b[i]); >printf("\n"); insertionSort(b, 10, sizeof(double), cmpDoubleDesc); for (i = 0; i < 10; i++) < printf("%.3f ", b[i]); >getch(); >

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

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

#include #include #include #include #define ERROR_DIV_BY_ZERO -2 #define EPSILON 0.000001f float doSum(float a, float b) < return a + b; >float doSub(float a, float b) < return a - b; >float doMul(float a, float b) < return a * b; >float doDiv(float a, float b) < if (fabs(b) return a / b; > void main() < float (*menu[4])(float, float); int op; float a, b; menu[0] = doSum; menu[1] = doSub; menu[2] = doMul; menu[3] = doDiv; printf("enter a: "); scanf("%f", &a); printf("enter b: "); scanf("%f", &b); printf("enter operation [0 - add, 1 - sub, 2 - mul, 3 - div]"); scanf("%d", &op); if (op >= 0 && op < 4) < printf("%.6f", menu[op](a, b)); >getch(); >

Точно также можно было создать массив динамически

void main() < float (**menu)(float, float) = NULL; int op; float a, b; menu = (float(**)(float, float)) malloc(4*sizeof(float(*)(float, float))); menu[0] = doSum; menu[1] = doSub; menu[2] = doMul; menu[3] = doDiv; printf("enter a: "); scanf("%f", &a); printf("enter b: "); scanf("%f", &b); printf("enter operation [0 - add, 1 - sub, 2 - mul, 3 - div]"); scanf("%d", &op); if (op >= 0 && op < 4) < printf("%.6f", menu[op](a, b)); >free(menu); getch(); >

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

#include #include #include #include #define ERROR_DIV_BY_ZERO -2 #define EPSILON 0.000001f typedef float (*operation)(float, float); float doSum(float a, float b) < return a + b; >float doSub(float a, float b) < return a - b; >float doMul(float a, float b) < return a * b; >float doDiv(float a, float b) < if (fabs(b) return a / b; > void main() < operation *menu = NULL; int op; float a, b; menu = (operation*) malloc(4*sizeof(operation)); menu[0] = doSum; menu[1] = doSub; menu[2] = doMul; menu[3] = doDiv; printf("enter a: "); scanf("%f", &a); printf("enter b: "); scanf("%f", &b); printf("enter operation [0 - add, 1 - sub, 2 - mul, 3 - div]"); scanf("%d", &op); if (op >= 0 && op < 4) < printf("%.6f", menu[op](a, b)); >free(menu); getch(); >

Ещё один пример: функция any возвращает 1, если в переданном массиве содержится хотя бы один элемент, удовлетворяющий условию pred и 0 в противном случае.

#include #include typedef int (*Predicat)(void*); int isBetweenInt(void* a) < return *((int*) a) >10 && *((int*) a) < 12; >int isBetweenDouble(void* a) < return *((double*) a) >10.0 && *((double*) a) < 12.0; >int any(void* arr, unsigned num, size_t size, Predicat pred) < unsigned i; char* ptr = (char*) arr; for (i = 0; i < num; i++) < if (pred(ptr + i*size)) < return 1; >> return 0; > void main() < int a[10] = ; double b[10] = ; printf("has 'a' any value > 10 and < 12? %d\n", any(a, 10, sizeof(int), isBetweenInt)); printf("has 'b' any value >10 and

qsort и bsearch

В библиотеке stdlib си имеется несколько функций, которые получают в качестве аргументов указатели на функции. Функция qsort получает такие же аргументы, как и написанная нами функция insertionSort: массив типа void, размер массива, размер элемента и указатель на функцию сравнения. Давайте посмотрим простой пример - сортировка массива строк:

#include #include #include #include #define SIZE 10 //qsort передаёт функции сравнения указатели на элементы. //но наш массив - это массив указателей, так что qsort будет //передавать указатели на указатели int cmpString(const void* a, const void* b) < return strcmp(*(const char**) a, *(const char**) b); >void main() < char *words[SIZE]; char buffer[128]; int i, length; printf("Enter %d words:", SIZE); for (i = 0; i < SIZE; i++) < scanf("%127s", buffer); length = strlen(buffer); words[i] = (char*) malloc(length+1); strcpy(words[i], buffer); >printf("\n"); qsort(words, SIZE, sizeof(char*), cmpString); for (i = 0; i < SIZE; i++) < printf("%s\n", words[i]); free(words[i]); >getch(); >

Функция bsearch проводит бинарный поиск в отсортированном массиве и получает указатель на функцию сравнения, такую же, как и функция qsort. В случае, если элемент найден, то она возвращает указатель на этот элемент, если элемент не найден, то NULL.

#include #include int cmpInt(const void* a, const void* b) < return *(int*) a - *(int*) b; >void main() < int a[10] = ; int elm; int *index; printf("Enter number to search: "); scanf("%d", &elm); index = (int*) bsearch(&elm, a, 10, sizeof(int), cmpInt); if (index == NULL) < printf("element not found"); >else < printf("index = %d", (index - a)); >getch(); >

ru-Cyrl 18- tutorial Sypachev S.S. 1989-04-14 sypachev_s_s@mail.ru Stepan Sypachev students

email

Всё ещё не понятно? – пиши вопросы на ящик

Урок №104. Указатели на функции

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

Оглавление:

  1. Указатели на функции
  2. Присваивание функции указателю на функцию
  3. Вызов функции через указатель на функцию
  4. Передача функций в качестве аргументов другим функциям
  5. Параметры по умолчанию в функциях
  6. Указатели на функции и псевдонимы типов
  7. Использование std::function в C++11
  8. Заключение
  9. Тест

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

Рассмотрим следующий фрагмент кода:

Идентификатор boo — это имя функции. Но какой её тип? Функции имеют свой собственный l-value тип. В этом случае это тип функции, который возвращает целочисленное значение и не принимает никаких параметров. Подобно переменным, функции также имеют свой адрес в памяти.

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

int boo ( ) // код функции boo() находится в ячейке памяти 002B1050
boo ( ) ; // переходим к адресу 002B1050

Одной из распространенных ошибок новичков является:

int boo ( ) // код функции boo() находится в ячейке памяти 002B1050
std :: cout << boo ; // мы хотим вызвать boo(), но вместо этого мы просто выводим boo!

Вместо вызова функции boo() и вывода возвращаемого значения мы, совершенно случайно, отправили указатель на функцию boo() непосредственно в std::cout. Что произойдет в этом случае?

Результат на моем компьютере:

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

int boo ( ) // код функции boo() находится в ячейке памяти 002B1050
std :: cout << reinterpret_cast < void * >( boo ) ; // указываем C++ конвертировать функцию boo() в указатель типа void

Так же, как можно объявить неконстантный указатель на обычную переменную, можно объявить и неконстантный указатель на функцию. Синтаксис создания неконстантного указателя на функцию, пожалуй, один из самых «уродливых» в языке C++:

// fcnPtr - это указатель на функцию, которая не принимает никаких аргументов и возвращает целочисленное значение

int ( * fcnPtr ) ( ) ;

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

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

Для создания константного указателя на функцию используйте const после звёздочки:

int ( * const fcnPtr ) ( ) ;

Если вы поместите const перед int, это будет означать, что функция, на которую указывает указатель, возвращает const int.

Присваивание функции указателю на функцию

Указатель на функцию может быть инициализирован функцией (и неконстантному указателю на функцию тоже можно присвоить функцию):

int ( * fcnPtr ) ( ) = boo ; // fcnPtr указывает на функцию boo()
fcnPtr = doo ; // fcnPtr теперь указывает на функцию doo()

Одна из распространенных ошибок, которую совершают новички:

fcnPtr = doo ( ) ;

Здесь мы фактически присваиваем возвращаемое значение из вызова функции doo() указателю fcnPtr , чего мы не хотим делать. Мы хотим, чтобы fcnPtr содержал адрес функции doo(), а не возвращаемое значение из doo(). Поэтому скобки здесь не нужны.

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

// Прототипы функций
double doo ( ) ;
int moo ( int a ) ;
// Присваивание значений указателям на функции
int ( * fcnPtr1 ) ( ) = boo ; // ок
int ( * fcnPtr2 ) ( ) = doo ; // не ок: тип указателя и тип возврата функции не совпадают!
double ( * fcnPtr4 ) ( ) = doo ; // ок
fcnPtr1 = moo ; // не ок: fcnPtr1 не имеет параметров, но moo() имеет
int ( * fcnPtr3 ) ( int ) = moo ; // ок

В отличие от фундаментальных типов данных, язык C++ неявно конвертирует функцию в указатель на функцию, если это необходимо (поэтому вам не нужно использовать оператор адреса & для получения адреса функции). Однако, язык C++ не будет неявно конвертировать указатель на функцию в указатель типа void или наоборот.

Вызов функции через указатель на функцию

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

int boo ( int a )
int ( * fcnPtr ) ( int ) = boo ; // присваиваем функцию boo() указателю fcnPtr
( * fcnPtr ) ( 7 ) ; // вызываем функцию boo(7), используя fcnPtr

Второй — через неявное разыменование:

int boo ( int a )
int ( * fcnPtr ) ( int ) = boo ; // присваиваем функцию boo() указателю fcnPtr
fcnPtr ( 7 ) ; // вызываем функцию boo(7), используя fcnPtr

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

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

Передача функций в качестве аргументов другим функциям

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

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

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

Вот наша сортировка методом выбора, рассмотренная на соответствующем уроке:

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

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