Ukazatele funkcí v programování C s příklady

Ukazatele dávají velké možnosti funkcím 'C', které jsme omezeni na vrácení jedné hodnoty. S parametry ukazatele mohou nyní naše funkce zpracovávat skutečná data spíše než kopie dat.

Aby bylo možné upravit skutečné hodnoty proměnných, předává volající příkaz adresy parametrům ukazatele ve funkci.

Funkce Ukazatele Příklad

Například následující program zamění dvě hodnoty po dvou:

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

Výstup:

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

Funkce Ukazatele Příklad

Program zamění skutečné hodnoty proměnných, protože funkce k nim přistupuje pomocí adresy ukazatel. Zde budeme diskutovat o průběhu programu:

  1. Deklarujeme funkci zodpovědnou za záměnu dvou hodnot proměnných, která bere jako parametry dva celočíselné ukazatele a při volání vrací libovolnou hodnotu.
  2. Ve funkci main deklarujeme a inicializujeme dvě celočíselné proměnné ('m' a 'n'), poté vypíšeme jejich hodnoty.
  3. Funkci swap() zavoláme předáním adresy dvou proměnných jako argumentů pomocí symbolu ampersand. Poté vytiskneme nové prohozené hodnoty proměnných.
  4. Zde definujeme obsah funkce swap(), která bere jako parametry dvě adresy celočíselných proměnných a deklarujeme dočasnou celočíselnou proměnnou použitou jako třetí úložný box pro uložení jedné z hodnotových proměnných, která bude vložena do druhé proměnné.
  5. Uložte obsah první proměnné označené 'a' do dočasné proměnné.
  6. Uložte druhou proměnnou označenou b do první proměnné označené a.
  7. Aktualizujte druhou proměnnou (označenou b) o hodnotu první proměnné uložené v dočasné proměnné.

Funkce s parametry pole

V C nemůžeme předat funkci pole podle hodnoty. Zatímco název pole je ukazatel (adresa), takže pouze předáme název pole funkci, což znamená předat poli ukazatel.

Uvažujeme například následující 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);}

Výstup:

 Total summation is 471

Zde vysvětlíme programový kód s jeho podrobnostmi

Funkce s parametry pole

  1. Deklarujeme a definujeme funkci add_array(), která bere jako parametry adresu pole (ukazatel) s číslem prvku a vrací celkový souhrn těchto prvků. Ukazatel se používá k iteraci prvků pole (pomocí zápisu p[k]) a sumaci akumulujeme v lokální proměnné, která se vrátí po iteraci celého pole prvků.
  2. Deklarujeme a inicializujeme celočíselné pole s pěti celočíselnými prvky. Celkový součet vytiskneme předáním názvu pole (který funguje jako adresa) a velikosti pole add_array()nazývaná funkce jako argumenty.

Funkce, které vracejí pole

V C můžeme vrátit ukazatel na pole, jako v následujícím programu:

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

Výstup:

1
2
3
4
5

A zde probereme detaily programu

Funkce, které vracejí pole

  1. Definujeme a deklarujeme funkci, která vrací adresu pole obsahující celočíselnou hodnotu a nebere žádné argumenty.
  2. Deklarujeme celočíselný ukazatel, který obdrží kompletní pole vytvořené po zavolání funkce a vytiskneme jeho obsah iterací celého pětiprvkového pole.

Všimněte si, že pro uložení adresy pole vrácené funkcí je definován ukazatel, nikoli pole. Všimněte si také, že když se z funkce vrací lokální proměnná, musíme ji ve funkci deklarovat jako statickou.

Ukazatele funkcí

Jak z definice víme, že ukazatele ukazují na adresu v libovolném paměťovém místě, mohou také ukazovat na začátek spustitelného kódu jako funkce v paměti.
Ukazatel na funkci je deklarován pomocí * , obecné prohlášení jeho deklarace je:

return_type (*function_name)(arguments)

Musíte si pamatovat, že závorky kolem (*název_funkce) jsou důležité, protože bez nich si kompilátor bude myslet, že název_funkce vrací ukazatel typu return_type.
Po definování ukazatele funkce jej musíme přiřadit funkci. Například následující program deklaruje běžnou funkci, definuje ukazatel na funkci, přiřadí ukazatel funkce k běžné funkci a poté funkci zavolá přes ukazatel:

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

Výstup:

Hi
Hi
Hi

Ukazatele funkcí v C

  1. Definujeme a deklarujeme standardní funkci, která při volání funkce vypíše Hi text k krát indikovaný parametrem times
  2. Definujeme funkci ukazatele (s její speciální deklarací), která přebírá celočíselný parametr a nic nevrací.
  3. Naši funkci ukazatele inicializujeme funkcí Hi_function, což znamená, že ukazatel ukazuje na funkci Hi_function().
  4. Spíše než standardní volání funkce poklepáním na název funkce s argumenty zavoláme pouze funkci ukazatele předáním čísla 3 jako argumentů a je to!

Mějte na paměti, že název funkce ukazuje na počáteční adresu spustitelného kódu jako název pole, který ukazuje na jeho první prvek. Proto jsou instrukce jako function_ptr = &Hi_function a (*funptr)(3) správné.
POZNÁMKA: Během přiřazování funkce a volání funkce není důležité vkládat operátor adresy & a operátor nepřímého směru *.

Pole funkčních ukazatelů

Řada ukazatelů funkcí může hrát roli přepínače nebo příkazu if pro rozhodování, jako v následujícím programu:

#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

Zde probíráme detaily programu:

Pole funkčních ukazatelů

  1. Deklarujeme a definujeme čtyři funkce které berou dva celočíselné argumenty a vracejí celočíselnou hodnotu. Tyto funkce sčítají, odečítají, násobí a rozdělují dva argumenty týkající se funkce, kterou uživatel volá.
  2. Deklarujeme 4 celá čísla pro zpracování operandů, typu operace a výsledku. Také deklarujeme pole čtyř funkčních ukazatelů. Každý funkční ukazatel prvku pole přebírá dva celočíselné parametry a vrací celočíselnou hodnotu.
  3. Každý prvek pole přiřadíme a inicializujeme již deklarovanou funkcí. Například třetí prvek, který je třetím ukazatelem funkce, bude ukazovat na funkci operace násobení.
  4. Vyhledáváme operandy a typ operace od uživatele zadaného pomocí klávesnice.
  5. Zavolali jsme příslušný prvek pole (ukazatel funkce) s argumenty a výsledek vygenerovaný příslušnou funkcí uložíme.

Instrukce int (*ope[4])(int, int); definuje pole ukazatelů funkcí. Každý prvek pole musí mít stejné parametry a návratový typ.
Výsledek příkazu = ope[volba](x, y); spustí příslušnou funkci podle volby provedené uživatelem. Dvě zadaná celá čísla jsou argumenty předané funkci.

Funkce využívající void ukazatele

Během deklarace funkcí se používají ukazatele na neplatné. Pro vrácení jakéhokoli typu používáme povolení typu void *. Pokud předpokládáme, že se naše parametry při předání funkci nemění, deklarujeme ji jako konst.
Například:

 void * cube (const void *);

Zvažte následující 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;}

Výsledek:

 4 cubed is 64

Zde probereme detaily programu:

Funkce využívající void ukazatele

  1. Definujeme a deklarujeme funkci, která vrací celočíselnou hodnotu a bere adresu neměnné proměnné bez konkrétního datového typu. Vypočítáme krychlovou hodnotu obsahové proměnné (x), na kterou ukazuje ukazatel num, a protože je to prázdný ukazatel, musíme ji zadat přetypovat na celočíselný datový typ pomocí specifického ukazatele zápisu (* datový typ) a vrátíme krychlová hodnota.
  2. Deklarujeme operand a výslednou proměnnou. Také inicializujeme náš operand s hodnotou „4“.
  3. Funkci kostky zavoláme předáním adresy operandu a návratovou hodnotu zpracujeme v proměnné result

Ukazatele funkcí jako argumenty

Další způsob, jak využít ukazatel funkce předáním jako argument jiné funkci, která se někdy nazývá „funkce zpětného volání“, protože přijímající funkce ji „zavolá zpět“.
V hlavičkovém souboru stdlib.h používá funkce Quicksort „qsort()“ tuto techniku, což je algoritmus určený pro třídění pole.

void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
  • void *base: void ukazatel na pole.
  • size_t num : Číslo prvku pole.
  • size_t width Velikost prvku.
  • int (*compare (const void *, const void *) : ukazatel funkce složený ze dvou argumentů a vrací 0, když mají argumenty stejnou hodnotu, <0, když arg1 přichází před arg2, a >0, když arg1 přichází za arg2.

Následující program třídí pole celých čísel od malého po velké číslo pomocí funkce 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;}

Výsledek:

 13 14 48 50 52

Zde probereme detaily programu:

Ukazatele funkcí jako argumenty

  1. Definujeme porovnávací funkci složenou ze dvou argumentů a vrací 0, když mají argumenty stejnou hodnotu, <0, když arg1 přichází před arg2, a >0, když arg1 je za arg2. Parametry jsou typ ukazatelů void přetypovaných na příslušný datový typ pole. (celé číslo)
  2. Definujeme a inicializujeme celočíselné pole Velikost pole je uložena v num a velikost každého prvku pole je uložena v proměnné šířky pomocí sizeof() predefined C operátor.
  3. Voláme qsort a předat název pole, velikost, šířku a porovnávací funkce definované dříve uživatelem, aby se naše pole seřadilo vzestupně. Porovnání bude provedeno tak, že se v každé iteraci převezmou dva prvky pole, dokud nebude setříděno celé pole.
  4. Vytiskneme prvky pole, abychom se ujistili, že naše pole je dobře seřazeno iterací celého pole pomocí pro smyčku.