Wskaźniki funkcji w programowaniu C z przykładami

Wskaźniki dają ogromne możliwości funkcjom „C”, które jesteśmy ograniczeni do zwracania jednej wartości. Dzięki parametrom wskaźnika nasze funkcje mogą teraz przetwarzać rzeczywiste dane, a nie kopię danych.

Aby zmodyfikować rzeczywiste wartości zmiennych, instrukcja wywołująca przekazuje adresy do parametrów wskaźników w funkcji.

Funkcje Wskaźniki Przykład

Na przykład następny program zamienia dwie wartości z dwóch:

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

Wyjście:

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

Funkcje Wskaźniki Przykład

Program zamienia rzeczywiste wartości zmiennych, ponieważ funkcja uzyskuje do nich dostęp za pomocą adresu wskaźnik. Tutaj omówimy proces programu:

  1. Deklarujemy funkcję odpowiedzialną za zamianę wartości dwóch zmiennych, która przyjmuje jako parametry dwa wskaźniki całkowite i zwraca dowolną wartość po wywołaniu.
  2. W funkcji main deklarujemy i inicjujemy dwie zmienne całkowite („m” i „n”), a następnie wypisujemy odpowiednio ich wartości.
  3. Wywołujemy funkcję swap(), przekazując adresy dwóch zmiennych jako argumenty za pomocą symbolu ampersand. Następnie drukujemy nowe zamienione wartości zmiennych.
  4. Tutaj definiujemy zawartość funkcji swap(), która przyjmuje dwa adresy zmiennych całkowitych jako parametry i deklarujemy tymczasową zmienną całkowitą, używaną jako trzecie pole do przechowywania, w którym zapisywana jest jedna ze zmiennych wartości, która zostanie umieszczona w drugiej zmiennej.
  5. Zapisz zawartość pierwszej zmiennej wskazanej przez „a” w zmiennej tymczasowej.
  6. Zapisz drugą zmienną wskazaną przez b w pierwszej zmiennej wskazanej przez a.
  7. Zaktualizuj drugą zmienną (oznaczoną przez b) o wartość pierwszej zmiennej zapisanej w zmiennej tymczasowej.

Funkcje z parametrami tablicy

W C nie możemy przekazać tablicy przez wartość do funkcji. Natomiast nazwa tablicy jest wskaźnikiem (adresem), więc po prostu przekazujemy nazwę tablicy do funkcji, co oznacza przekazanie wskaźnika do tablicy.

Rozważmy na przykład następujący program:

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);}

Wyjście:

 Total summation is 471

Tutaj wyjaśnimy kod programu wraz z jego szczegółami

Funkcje z parametrami tablicy

  1. Deklarujemy i definiujemy funkcję add_array(), która przyjmuje jako parametry adres tablicy (wskaźnik) z liczbą jej elementów i zwraca całkowitą sumę tych elementów. Wskaźnik służy do iteracji elementów tablicy (w notacji p[k]), a sumowanie gromadzimy w zmiennej lokalnej, która zostanie zwrócona po iteracji całej tablicy elementów.
  2. Deklarujemy i inicjujemy tablicę liczb całkowitych zawierającą pięć elementów całkowitych. Wypisujemy całkowite sumowanie, przekazując nazwę tablicy (która pełni rolę adresu) i rozmiar tablicy do dodaj_tablicę()nazywaną funkcją jako argumentami.

Funkcje zwracające tablicę

W języku C możemy zwrócić wskaźnik do tablicy, jak w poniższym programie:

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

Wyjście:

1
2
3
4
5

A tutaj omówimy szczegóły programu

Funkcje zwracające tablicę

  1. Definiujemy i deklarujemy funkcję, która zwraca adres tablicy zawierający wartość całkowitą i nie przyjmuje żadnych argumentów.
  2. Deklarujemy wskaźnik całkowity, który otrzymuje całą tablicę zbudowaną po wywołaniu funkcji i wypisujemy jej zawartość poprzez iterację całej pięcioelementowej tablicy.

Zauważ, że do przechowywania adresu tablicy zwracanego przez funkcję zdefiniowano wskaźnik, a nie tablicę. Zauważ także, że gdy funkcja zwraca zmienną lokalną, musimy zadeklarować ją w funkcji jako statyczną.

Wskaźniki funkcji

Jak wiemy z definicji, że wskaźniki wskazują adres w dowolnej lokalizacji pamięci, mogą również wskazywać na początek kodu wykonywalnego jako funkcje w pamięci.
Wskaźnik do funkcji deklarowany jest za pomocą *, ogólna instrukcja jego deklaracji wygląda następująco:

return_type (*function_name)(arguments)

Musisz pamiętać, że nawiasy wokół (*nazwa_funkcji) są ważne, ponieważ bez nich kompilator będzie myślał, że nazwa_funkcji zwraca wskaźnik typu zwracanego.
Po zdefiniowaniu wskaźnika funkcji musimy przypisać go do funkcji. Na przykład, następny program deklaruje zwykłą funkcję, definiuje wskaźnik funkcji, przypisuje wskaźnik funkcji do zwykłej funkcji, a następnie wywołuje funkcję przez wskaźnik:

#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");}

Wyjście:

Hi
Hi
Hi

Funkcje Wskaźniki w C

  1. Definiujemy i deklarujemy standardową funkcję, która przy wywołaniu funkcji wypisuje tekst Hi k razy wskazanych przez liczbę parametrów
  2. Definiujemy funkcję wskaźnikową (wraz ze specjalną deklaracją), która przyjmuje parametr całkowity i nic nie zwraca.
  3. Inicjujemy naszą funkcję wskaźnikową funkcją Hi_, co oznacza, że ​​wskaźnik wskazuje na funkcję Hi_function().
  4. Zamiast standardowego wywoływania funkcji poprzez dodanie argumentów do nazwy funkcji, wywołujemy tylko funkcję wskaźnika, przekazując liczbę 3 jako argumenty i to wszystko!

Należy pamiętać, że nazwa funkcji wskazuje na adres początkowy kodu wykonywalnego, podobnie jak nazwa tablicy, która wskazuje na jej pierwszy element. Dlatego instrukcje takie jakfunction_ptr = &Hi_function i (*funptr)(3) są poprawne.
UWAGA: Podczas przypisywania funkcji i wywoływania funkcji nie jest istotne wstawianie operatora adresu & oraz operatora pośredniego *.

Tablica wskaźników funkcji

Tablica wskaźników funkcji może pełnić rolę przełącznika lub instrukcji if przy podejmowaniu decyzji, jak w następnym programie:

#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

Tutaj omawiamy szczegóły programu:

Tablica wskaźników funkcji

  1. Deklarujemy i definiujemy cztery Funkcje które przyjmują dwa argumenty będące liczbami całkowitymi i zwracają wartość całkowitą. Funkcje te dodają, odejmuje, mnożą i dzielą dwa argumenty dotyczące tego, która funkcja jest wywoływana przez użytkownika.
  2. Deklarujemy 4 liczby całkowite do obsługi operandów, typu operacji i wyniku. Ponadto deklarujemy tablicę czterech wskaźników funkcji. Każdy wskaźnik funkcji elementu tablicy przyjmuje dwa parametry całkowite i zwraca wartość całkowitą.
  3. Przypisujemy i inicjujemy każdy element tablicy funkcją już zadeklarowaną. Na przykład trzeci element, który jest wskaźnikiem trzeciej funkcji, będzie wskazywał na funkcję operacji mnożenia.
  4. Szukamy operandów i typu operacji wpisywanych przez użytkownika za pomocą klawiatury.
  5. Wywołujemy odpowiedni element tablicy (wskaźnik funkcji) wraz z argumentami i przechowujemy wynik wygenerowany przez odpowiednią funkcję.

Instrukcja int (*ope[4])(int, int); definiuje tablicę wskaźników funkcji. Każdy element tablicy musi mieć te same parametry i typ zwracany.
Wynik instrukcji = ope[wybór](x, y); uruchamia odpowiednią funkcję zgodnie z wyborem dokonanym przez użytkownika. Dwie wprowadzone liczby całkowite stanowią argumenty przekazywane do funkcji.

Funkcje wykorzystujące puste wskaźniki

Wskaźniki puste są używane podczas deklaracji funkcji. Używamy typu zwrotu void *, który pozwala na zwrócenie dowolnego typu. Jeśli założymy, że nasze parametry nie zmieniają się podczas przejścia do funkcji, deklarujemy to jako const.
Na przykład:

 void * cube (const void *);

Rozważ następujący program:

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

Wynik:

 4 cubed is 64

Tutaj omówimy szczegóły programu:

Funkcje wykorzystujące puste wskaźniki

  1. Definiujemy i deklarujemy funkcję zwracającą wartość całkowitą i przyjmującą adres zmiennej niezmiennej bez określonego typu danych. Obliczamy wartość sześcianu zmiennej treści (x) wskazywanej przez wskaźnik num, a ponieważ jest to wskaźnik typu void, musimy wpisać rzutowanie go na typ danych całkowitych przy użyciu wskaźnika o określonej notacji (* typ danych) i zwracamy wartość kostki.
  2. Deklarujemy operand i zmienną wynikową. Ponadto inicjujemy nasz operand wartością „4”.
  3. Wywołujemy funkcję kostki, przekazując adres operandu, a zwracaną wartość obsługujemy w zmiennej wynikowej

Wskaźniki funkcji jako argumenty

Inny sposób wykorzystania wskaźnika funkcji poprzez przekazanie go jako argumentu innej funkcji, zwanej czasem „funkcją wywołania zwrotnego”, ponieważ funkcja odbierająca „wywołuje go z powrotem”.
W pliku nagłówkowym stdlib.h funkcja Quicksort „qsort()” wykorzystuje tę technikę, która jest algorytmem przeznaczonym do sortowania tablicy.

void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
  • void *base: pusty wskaźnik do tablicy.
  • size_t num : Numer elementu tablicy.
  • size_t szerokość Rozmiar elementu.
  • int (*compare (const void *, const void *): wskaźnik funkcji złożony z dwóch argumentów i zwraca 0, gdy argumenty mają tę samą wartość, <0, gdy arg1 występuje przed arg2 i >0, gdy arg1 występuje po arg2.

Poniższy program sortuje tablicę liczb całkowitych od najmniejszej do największej liczby przy użyciu funkcji 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;}

Wynik:

 13 14 48 50 52

Tutaj omówimy szczegóły programu:

Wskaźniki funkcji jako argumenty

  1. Definiujemy funkcję porównania złożoną z dwóch argumentów i zwraca 0, gdy argumenty mają tę samą wartość, <0, gdy arg1 występuje przed arg2 i >0, gdy arg1 występuje po arg2. Parametry są pustymi wskaźnikami rzutowanymi na odpowiedni typ danych tablicowych (liczba całkowita)
  2. Definiujemy i inicjujemy tablicę liczb całkowitych. Rozmiar tablicy przechowywany jest w pliku num zmienną, a rozmiar każdego elementu tablicy jest przechowywany w zmiennej szerokości przy użyciu predefiniowanej funkcji sizeof(). Operator C.
  3. Nazywamy sortuj funkcję i przekazać nazwę tablicy, jej rozmiar, szerokość i funkcję porównawczą zdefiniowaną wcześniej przez użytkownika, aby posortować naszą tablicę w kolejności rosnącej. Porównanie zostanie przeprowadzone poprzez pobieranie w każdej iteracji dwóch elementów tablicy, aż cała tablica zostanie posortowana.
  4. Aby mieć pewność, że nasza tablica jest dobrze posortowana, drukujemy elementy tablicy, iterując całą tablicę za pomocą dla pętli.