Функционални указатели в C програмиране с примери

Указателите дават големи възможности на "C" функциите, които са ограничени да връщат една стойност. С параметрите на указателя нашите функции вече могат да обработват действителни данни, а не копие на данни.

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

Пример за указатели на функции

Например, следващата програма разменя две стойности от две:

void swap (int *a, int *b);
int main() {
  int m = 25;
  int n = 100;
  printf("m is %d, n is %d\n", m, n);
  swap(&m, &n);
  printf("m is %d, n is %d\n", m, n);
  return 0;}
void swap (int *a, int *b) {
  int temp;
  temp = *a;
  *a = *b;
  *b = temp;}
}

Изход:

m is 25, n is 100
m is 100, n is 25

Пример за указатели на функции

Програмата разменя действителните стойности на променливите, тъй като функцията осъществява достъп до тях чрез използване на адрес показалка. Тук ще обсъдим процеса на програмата:

  1. Ние декларираме функцията, отговорна за размяната на стойностите на двете променливи, която приема два целочислени указателя като параметри и връща всяка стойност, когато бъде извикана.
  2. В основната функция ние декларираме и инициализираме две цели променливи ('m' и 'n'), след което съответно отпечатваме техните стойности.
  3. Извикваме функцията swap(), като предаваме адреса на двете променливи като аргументи, използвайки символа амперсанд. След това отпечатваме новите разменени стойности на променливите.
  4. Тук дефинираме съдържанието на функцията swap(), която приема два адреса на целочислени променливи като параметри и декларира временна целочислена променлива, използвана като трета кутия за съхранение, за да запази една от стойностните променливи, които ще бъдат поставени във втората променлива.
  5. Запазете съдържанието на първата променлива, отбелязана с 'a' във временната променлива.
  6. Съхранявайте втората променлива, означена с b, в първата променлива, означена с a.
  7. Актуализирайте втората променлива (посочена с b) със стойността на първата променлива, записана във временната променлива.

Функции с параметри на масива

В C не можем да предадем масив по стойност към функция. Докато името на масива е указател (адрес), така че ние просто предаваме име на масив към функция, което означава да предадем указател към масива.

Например, ние разглеждаме следната програма:

int add_array (int *a, int num_elements);
int main() {
  int Tab[5] = {100, 220, 37, 16, 98};
  printf("Total summation is %d\n", add_array(Tab, 5)); 
  return 0;}
int add_array (int *p, int size) {
  int total = 0;
  int k;
  for (k = 0; k < size; k++) {
    total += p[k];  /* it is equivalent to total +=*p ;p++; */}
 return (total);}

Изход:

 Total summation is 471

Тук ще обясним програмния код с неговите подробности

Функции с параметри на масива

  1. Ние декларираме и дефинираме функцията add_array(), която приема адрес на масив (указател) с номера на елементите като параметри и връща общото натрупано сумиране на тези елементи. Указателят се използва за повторение на елементите на масива (използвайки нотацията p[k]) и ние натрупваме сумата в локална променлива, която ще бъде върната след повторение на целия масив от елементи.
  2. Ние декларираме и инициализираме целочислен масив с пет цели числа. Отпечатваме общата сума, като предаваме името на масива (което действа като адрес) и размера на масива на add_array()наречена функция като аргументи.

Функции, които връщат масив

В C можем да върнем указател към масив, както в следната програма:

#include <stdio.h>
int * build_array();
int main() {
  int *a;
  a = build_array(); /* get first 5 even numbers */
  for (k = 0; k < 5; k++)
    printf("%d\n", a[k]);
  return 0;}
int * build_array() {
  static int Tab[5]={1,2,3,4,5};
   return (Tab);}

Изход:

1
2
3
4
5

И тук ще обсъдим подробностите по програмата

Функции, които връщат масив

  1. Ние дефинираме и декларираме функция, която връща адрес на масив, съдържащ цяло число и не приема никакви аргументи.
  2. Ние декларираме целочислен указател, който получава пълния масив, изграден след извикването на функцията, и отпечатваме съдържанието му, като итерираме целия масив от пет елемента.

Забележете, че за съхраняване на адреса на масива, върнат от функцията, е дефиниран указател, а не масив. Забележете също, че когато локална променлива се връща от функция, трябва да я декларираме като статична във функцията.

Функционални указатели

Както знаем по дефиниция, че указателите сочат към адрес във всяко място в паметта, те също могат да сочат към началото на изпълним код като функции в паметта.
Указател към функция се декларира с *, общото изявление на неговата декларация е:

return_type (*function_name)(arguments)

Трябва да запомните, че скобите около (*function_name) са важни, защото без тях компилаторът ще мисли, че function_name връща указател на return_type.
След като дефинираме указателя на функцията, трябва да го присвоим на функция. Например, следващата програма декларира обикновена функция, дефинира указател на функция, присвоява указателя на функцията на обикновената функция и след това извиква функцията чрез указателя:

#include <stdio.h>
void Hi_function (int times); /* function */
int main() {
  void (*function_ptr)(int);  /* function pointer Declaration */
  function_ptr = Hi_function;  /* pointer assignment */
  function_ptr (3);  /* function call */
 return 0;}
void Hi_function (int times) {
  int k;
  for (k = 0; k < times; k++) printf("Hi\n");}

Изход:

Hi
Hi
Hi

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

  1. Ние дефинираме и декларираме стандартна функция, която отпечатва Hi текст k пъти, указан от параметъра пъти, когато функцията се извика
  2. Ние дефинираме указателна функция (с нейната специална декларация), която приема целочислен параметър и не връща нищо.
  3. Инициализираме нашата указателна функция с Hi_function, което означава, че указателят сочи към Hi_function().
  4. Вместо стандартната функция да се извиква чрез залепване на името на функцията с аргументи, ние извикваме само функцията на указателя, като предаваме числото 3 като аргументи и това е!

Имайте предвид, че името на функцията сочи към началния адрес на изпълнимия код като име на масив, което сочи към неговия първи елемент. Следователно инструкции като function_ptr = &Hi_function и (*funptr)(3) са правилни.
ЗАБЕЛЕЖКА: Не е важно да вмъквате адресния оператор & и индиректния оператор * по време на присвояването на функция и извикването на функция.

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

Масив от функционални указатели може да играе ролята на превключвател или оператор if за вземане на решение, както в следващата програма:

#include <stdio.h>
int sum(int num1, int num2);
int sub(int num1, int num2);
int mult(int num1, int num2);
int div(int num1, int num2);

int main() 
{  int x, y, choice, result;
  int (*ope[4])(int, int);
  ope[0] = sum;
  ope[1] = sub;
  ope[2] = mult;
  ope[3] = div;
  printf("Enter two integer numbers: ");
  scanf("%d%d", &x, &y);
  printf("Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: ");
  scanf("%d", &choice);
  result = ope[choice](x, y);
  printf("%d", result);
return 0;}

int sum(int x, int y) {return(x + y);}
int sub(int x, int y) {return(x - y);}
int mult(int x, int y) {return(x * y);}
int div(int x, int y) {if (y != 0) return (x / y); else  return 0;}
Enter two integer numbers: 13 48
Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: 2
624

Тук обсъждаме подробностите за програмата:

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

  1. Декларираме и дефинираме четири функции които приемат два целочислени аргумента и връщат цяло число. Тези функции добавят, изваждат, умножават и разделят двата аргумента относно това коя функция се извиква от потребителя.
  2. Декларираме 4 цели числа за обработка съответно на операнди, тип операция и резултат. Също така, ние декларираме масив от четири функционални указателя. Всеки функционален указател на елемент от масив приема два целочислени параметъра и връща цяло число.
  3. Ние присвояваме и инициализираме всеки елемент от масива с вече декларираната функция. Например, третият елемент, който е третият функционален указател, ще сочи към функцията за операция на умножение.
  4. Ние търсим операнди и тип операция от потребителя, въведен с клавиатурата.
  5. Извикахме съответния елемент на масива (указател на функция) с аргументи и съхраняваме резултата, генериран от съответната функция.

Инструкцията int (*ope[4])(int, int); дефинира масива от указатели на функции. Всеки елемент от масива трябва да има едни и същи параметри и тип на връщане.
Резултатът от израза = ope[избор](x, y); изпълнява подходящата функция според избора, направен от потребителя Двете въведени цели числа са аргументите, предадени на функцията.

Функции, използващи void указатели

Void указателите се използват по време на декларации на функции. Ние използваме void * тип за връщане позволява да се връща всеки тип. Ако приемем, че нашите параметри не се променят при преминаване към функция, ние я декларираме като const.
Например:

 void * cube (const void *);

Помислете за следната програма:

#include <stdio.h>
void* cube (const void* num);
int main() {
  int x, cube_int;
  x = 4;
  cube_int = cube (&x);
  printf("%d cubed is %d\n", x, cube_int);
  return 0;}

void* cube (const void *num) {
  int result;
  result = (*(int *)num) * (*(int *)num) * (*(int *)num);
  return result;}

Резултат:

 4 cubed is 64

Тук ще обсъдим подробностите по програмата:

Функции, използващи void указатели

  1. Ние дефинираме и декларираме функция, която връща целочислена стойност и взема адрес на непроменлива променлива без конкретен тип данни. Изчисляваме стойността на куба на променливата на съдържанието (x), посочена от указателя num, и тъй като е празен указател, трябва да напишем прехвърляне към целочислен тип данни, използвайки указател със специфична нотация (* тип данни) и връщаме стойността на куба.
  2. Декларираме операнда и резултатната променлива. Освен това инициализираме нашия операнд със стойност „4“.
  3. Извикваме функцията на куба, като предаваме адреса на операнда и обработваме върнатата стойност в резултатната променлива

Функционални указатели като аргументи

Друг начин за използване на указател на функция, като го подадете като аргумент на друга функция, понякога наричана „функция за обратно извикване“, защото приемащата функция „го извиква обратно“.
В заглавния файл stdlib.h функцията Quicksort „qsort()“ използва тази техника, която е алгоритъм, предназначен за сортиране на масив.

void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
  • void *base : void указател към масива.
  • size_t num : Номерът на елемента на масива.
  • size_t width Размерът на елемента.
  • int (*compare (const void *, const void *) : указател на функция, съставен от два аргумента и връща 0, когато аргументите имат еднаква стойност, <0, когато arg1 идва преди arg2, и >0, когато arg1 идва след arg2.

Следната програма сортира масив от цели числа от малко до голямо число с помощта на функцията qsort():

#include <stdio.h>
#include <stdlib.h>
int compare (const void *, const void *); 
int main() {
  int arr[5] = {52, 14, 50, 48, 13};
  int num, width, i;
  num = sizeof(arr)/sizeof(arr[0]);
  width = sizeof(arr[0]);
  qsort((void *)arr, num, width, compare);
  for (i = 0; i < 5; i++)
    printf("%d ", arr[ i ]);
  return 0;}
int compare (const void *elem1, const void *elem2) {
  if ((*(int *)elem1) == (*(int *)elem2))  return 0;
  else if ((*(int *)elem1) < (*(int *)elem2)) return -1;
  else return 1;}

Резултат:

 13 14 48 50 52

Тук ще обсъдим подробностите по програмата:

Функционални указатели като аргументи

  1. Ние дефинираме функция за сравнение, съставена от два аргумента и връща 0, когато аргументите имат една и съща стойност, <0, когато arg1 идва преди arg2, и >0, когато arg1 идва след arg2. Параметрите са тип празни указатели, прехвърлени към съответния тип данни на масив (цяло число)
  2. Ние дефинираме и инициализираме масив с цели числа. Размерът на масива се съхранява в бр променлива и размерът на всеки елемент от масива се съхранява в променлива за ширина, използвайки sizeof() предварително дефиниран C оператор.
  3. Ние наричаме qsort функция и предаваме името на масива, размера, ширината и функцията за сравнение, дефинирани преди това от потребителя, за да сортираме нашия масив във възходящ ред. Сравнението ще се извърши, като във всяка итерация се вземат два елемента от масив, докато целият масив бъде сортиран.
  4. Отпечатваме елементите на масива, за да сме сигурни, че нашият масив е добре сортиран, като итерираме целия масив, използвайки за цикъл.