Funkcijski pokazivači u C programiranju s primjerima

⚡ Pametni sažetak

Pokazivači funkcija u C-u pohranjuju adresu izvršnog koda, omogućujući programima da neizravno pozivaju funkcije, prosljeđuju ih kao argumente, grade tablice otpreme iz nizova i implementiraju povratne pozive poput rutine usporedbe qsort za fleksibilne, višekratno upotrebljive dizajne.

  • 🔗 Izjava: Pokazivač funkcije koristi oblik return_type(*naziv)(argumenti); zagrade oko naziva su obavezne.
  • 🔄 Zamijeni po adresi: Predavanje varijabilnih adresa parametrima pokazivača omogućuje funkciji promjenu izvornih vrijednosti pozivatelja, kao u primjeru swap-a.
  • ???? Nizovi: Naziv polja je već pokazivač, tako da funkcije mogu primiti polje ili vratiti pokazivač na statično polje.
  • 🎛️ Tablice za otpremu: Niz pokazivača funkcija zamjenjuje duge lance switch ili if indeksiranjem izravno na odabranu operaciju.
  • 🧊 prazni pokazivači: Parametar void * ili povratni tip prihvaća bilo koji tip, koji funkcija vraća prije upotrebe.
  • 📞 Povratni pozivi: Predavanje pokazivača funkcije kao argumenta pokreće povratne funkcije poput funkcije usporedbe qsort.
  • 🤖 AI pomoć: GitHub Copilot i AI asistenti scaffoldiraju sintaksu pokazivača funkcija, definicije tipova i potpise povratnih poziva, a zatim označavaju neusklađene parametre.

Pokazivači funkcija u C-u

Pokazivači daju velike mogućnosti 'C' funkcijama koje su ograničene na vraćanje jedne vrijednosti. S parametrima pokazivača, naše funkcije sada mogu obraditi stvarne podatke umjesto kopije podataka.

Kako bi se modificirale stvarne vrijednosti varijabli, pozivna izjava prosljeđuje adrese parametrima pokazivača u funkciji.

Primjer pokazivača funkcija

Na primjer, sljedeći program mijenja dvije vrijednosti od dva:

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

Izlaz:

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

Primjer pokazivača funkcija

Program mijenja stvarne vrijednosti varijabli jer im funkcija pristupa putem adrese pokazivač. Ovdje ćemo razgovarati o procesu programa:

  1. Deklariramo funkciju odgovornu za zamjenuping dvije varijable vrijednosti, koja uzima dva cjelobrojna pokazivača kao parametre i vraća bilo koju vrijednost kada se pozove.
  2. U glavnoj funkciji deklariramo i inicijaliziramo dvije cjelobrojne varijable ('m' i 'n'), a zatim ispisujemo njihove vrijednosti.
  3. Funkciju swap() pozivamo prosljeđivanjem adrese dviju varijabli kao argumenata pomoću simbola ampersend. Nakon toga ispisujemo nove zamijenjene vrijednosti varijabli.
  4. Ovdje definiramo sadržaj funkcije swap() koja uzima dvije adrese cjelobrojnih varijabli kao parametre i deklariramo privremenu cjelobrojnu varijablu koja se koristi kao treća kutija za pohranu za spremanje jedne od varijabli vrijednosti koje će se staviti u drugu varijablu.
  5. Sačuvajte sadržaj prve varijable označene s 'a' u privremenoj varijabli.
  6. Pohranite drugu varijablu označenu s b u prvu varijablu označenu s a.
  7. Ažurirajte drugu varijablu (označenu s b) vrijednošću prve varijable spremljene u privremenoj varijabli.

Funkcije s parametrima polja

U C-u ne možemo proslijediti niz prema vrijednosti u funkciju. Dok je naziv niza pokazivač (adresa), tako da samo prosljeđujemo naziv niza funkciji što znači proslijediti pokazivač na niz.

Na primjer, razmatramo sljedeći 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);}

Izlaz:

 Total summation is 471

Ovdje ćemo objasniti programski kod s detaljima

Funkcije s parametrima polja

  1. Deklariramo i definiramo funkciju add_array() koja uzima adresu polja (pokazivač) s brojem elemenata kao parametre i vraća ukupni akumulirani zbroj tih elemenata. Pokazivač se koristi za ponavljanje elemenata niza (koristeći p[k] notaciju), a zbroj akumuliramo u lokalnoj varijabli koja će biti vraćena nakon ponavljanja cijelog niza elemenata.
  2. Deklariramo i inicijaliziramo niz cjelobrojnih brojeva s pet cjelobrojnih elemenata. Ukupni zbroj ispisujemo prosljeđujući naziv niza (koji djeluje kao adresa) i veličinu niza add_array()zove funkcija kao argumenti.

Funkcije koje vraćaju niz

U C-u možemo vratiti pokazivač na niz, kao u sljedećem 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);}

Izlaz:

1
2
3
4
5

A ovdje ćemo razgovarati o detaljima programa

Funkcije koje vraćaju niz

  1. Definiramo i deklariramo funkciju koja vraća adresu niza koja sadrži cjelobrojnu vrijednost i ne uzima nikakve argumente.
  2. Deklariramo cjelobrojni pokazivač koji prima cijeli niz izgrađen nakon poziva funkcije i ispisujemo njegov sadržaj iteracijom čitavog niza od pet elemenata.

Primijetite da je pokazivač, a ne niz, definiran za pohranjivanje adrese niza koju vraća funkcija. Također primijetite da kada se lokalna varijabla vraća iz funkcije, moramo je deklarirati kao statičnu u funkciji.

Funkcijski pokazivači

Kako po definiciji znamo da pokazivači pokazuju na adresu u bilo kojoj memorijskoj lokaciji, oni također mogu pokazivati ​​na početak izvršnog koda kao funkcije u memoriji.
Pokazivač na funkciju je deklariran sa *, opći iskaz njegove deklaracije je:

return_type (*function_name)(arguments)

Morate zapamtiti da su zagrade oko (*function_name) važne jer će bez njih prevoditelj misliti da function_name vraća pokazivač return_type.
Nakon definiranja pokazivača funkcije, moramo ga dodijeliti funkciji. Na primjer, sljedeći program deklarira običnu funkciju, definira pokazivač funkcije, dodjeljuje pokazivač funkcije običnoj funkciji i nakon toga poziva funkciju preko pokazivača:

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

Izlaz:

Hi
Hi
Hi

Funkcijski pokazivači u C-u

  1. Definiramo i deklariramo standardnu ​​funkciju koja ispisuje Hi tekst k puta naznačeno parametrom puta kada se funkcija pozove
  2. Definiramo pokazivačku funkciju (sa svojom posebnom deklaracijom) koja uzima cjelobrojni parametar i ne vraća ništa.
  3. Inicijaliziramo našu funkciju pokazivača s Hi_function što znači da pokazivač pokazuje na Hi_function().
  4. Umjesto standardnog poziva funkcije od strane taping naziv funkcije s argumentima, pozivamo samo pokazivačku funkciju prosljeđujući broj 3 kao argumente i to je to!

Imajte na umu da naziv funkcije pokazuje na početnu adresu izvršnog koda poput naziva niza koji pokazuje na njegov prvi element. Stoga su upute poput function_ptr = &Hi_function i (*funptr)(3) točne.
NAPOMENA: Nije važno umetnuti adresni operator & i indirektni operator * tijekom dodjele funkcije i poziva funkcije.

Niz pokazivača funkcija

Niz funkcijskih pokazivača može igrati ulogu prekidača ili naredbe if za donošenje odluke, kao u sljedećem 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

Ovdje raspravljamo o detaljima programa:

Niz pokazivača funkcija

  1. Proglašavamo i definiramo četiri Funkcije koje uzimaju dva cjelobrojna argumenta i vraćaju cjelobrojnu vrijednost. Ove funkcije zbrajaju, podračunavajutract, pomnožite i podijelite dva argumenta u vezi s tim koju funkciju korisnik poziva.
  2. Deklariramo 4 cijela broja za rukovanje operandima, tipom operacije i rezultatom. Također, deklariramo niz od četiri funkcijska pokazivača. Svaki funkcijski pokazivač elementa niza uzima dva cjelobrojna parametra i vraća cjelobrojnu vrijednost.
  3. Svaki element niza dodjeljujemo i inicijaliziramo s već deklariranom funkcijom. Na primjer, treći element koji je pokazivač treće funkcije pokazat će na funkciju operacije množenja.
  4. Tražimo operande i vrstu operacije od korisnika tipkanog tipkovnicom.
  5. Pozvali smo odgovarajući element niza (Function pointer) s argumentima, a rezultat koji je generirala odgovarajuća funkcija pohranili smo.

Instrukcija int (*ope[4])(int, int); definira niz funkcijskih pokazivača. Svaki element niza mora imati iste parametre i vrstu povrata.
Rezultat iskaza = ope[izbor](x, y); pokreće odgovarajuću funkciju prema izboru korisnika. Dva unesena cijela broja su argumenti proslijeđeni funkciji.

Funkcije koje koriste void pokazivače

Void pokazivači se koriste tijekom deklaracije funkcija. Koristimo void * povratni tip dopušta vraćanje bilo kojeg tipa. Ako pretpostavimo da se naši parametri ne mijenjaju prilikom prijelaza na funkciju, deklariramo je kao const.
Na primjer:

 void * cube (const void *);

Razmotrite sljedeći 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;}

Rezultat:

 4 cubed is 64

Ovdje ćemo razgovarati o detaljima programa:

Funkcije koje koriste void pokazivače

  1. Definiramo i deklariramo funkciju koja vraća cjelobrojnu vrijednost i uzima adresu nepromjenjive varijable bez specifičnog tipa podataka. Izračunavamo kubnu vrijednost varijable sadržaja (x) na koju pokazuje pokazivač num, a budući da je to pokazivač void, moramo ga utipkati pretvoriti u cjelobrojni tip podataka pomoću pokazivača specifične notacije (* datatype), i vraćamo vrijednost kocke.
  2. Deklariramo operand i varijablu rezultata. Također, inicijaliziramo naš operand s vrijednošću "4."
  3. Funkciju kocke pozivamo prosljeđivanjem adrese operanda i obrađujemo povratnu vrijednost u varijabli rezultata

Funkcijski pokazivači kao argumenti

Još jedan način za iskorištavanje pokazivača funkcije prosljeđivanjem kao argumenta drugoj funkciji koja se ponekad naziva "funkcija povratnog poziva" jer je funkcija primatelja "poziva natrag".
U datoteci zaglavlja stdlib.h funkcija Quicksort "qsort()" koristi ovu tehniku ​​koja je algoritam posvećen razvrstavanju niza.

void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
  • void *baza : void pokazivač na niz.
  • size_t num : Broj elementa niza.
  • size_t width Veličina elementa.
  • int (*usporedi (const void *, const void *) : pokazivač funkcije sastavljen od dva argumenta i vraća 0 kada argumenti imaju istu vrijednost, <0 kada arg1 dolazi prije arg2, i >0 kada arg1 dolazi nakon arg2.

Sljedeći program sortira niz cijelih brojeva od malog do velikog broja pomoću funkcije 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;}

Rezultat:

 13 14 48 50 52

Ovdje ćemo razgovarati o detaljima programa:

Funkcijski pokazivači kao argumenti

  1. Definiramo funkciju usporedbe koja se sastoji od dva argumenta i vraća 0 kada argumenti imaju istu vrijednost, <0 kada arg1 dolazi prije arg2, i >0 kada arg1 dolazi nakon arg2. Parametri su tip void pokazivača koji se pretvaraju u odgovarajući tip podataka niza (cijeli broj)
  2. Definiramo i inicijaliziramo niz cijelih brojeva. Veličina niza pohranjuje se u broj varijabla, a veličina svakog elementa niza pohranjuje se u varijabli širine pomoću unaprijed definiranog sizeof() C operator.
  3. Pozivamo funkciju qsort i prosljeđujemo naziv polja, veličinu, širinu i funkciju usporedbe koju je prethodno definirao korisnik kako bismo sortirali naše polje uzlaznim redoslijedom. Usporedba će se izvršiti uzimanjem dva elementa polja u svakoj iteraciji dok se cijeli niz ne sortira.
  4. Ispisujemo elemente niza kako bismo bili sigurni da je naš niz dobro sortiran ponavljanjem cijelog niza koristeći za petlju.

Pitanja i odgovori

Da. Pokazivač funkcije mora točno odgovarati tipu povrata i popisu parametara ciljne funkcije. Dodjeljivanje funkcije s drugačijim potpisom je nedefinirano ponašanje, čak i ako se kompajlira, jer bi poziv netočno gurao i čitao argumente tijekom izvođenja.

typedef daje pokazivaču složene funkcije kratko ime. Zapisivanje typedef int(*Operacija)(int, int); omogućuje vam deklariranje Operadodavanje; umjesto ponavljanja pune sintakse pokazivača, što čini nizove, parametre i članove strukture puno lakšim za čitanje.

Pokazivači funkcija pokreću povratne pozive, tablice skokova ili otprema, sustave vođene dodacima i događajima, automate stanja i bibliotečke rutine poput qsort-a i bsearch-a. OperaSustavi za upravljanje i ugrađeni firmver oslanjaju se na njih za odabir ponašanja tijekom izvođenja bez dugih uvjetnih lanaca.

Pokazivač podataka sadrži adresu varijable u memoriji, dok pokazivač funkcije sadrži ulaznu adresu izvršnog koda. Pozivate pokazivač funkcije za pokretanje funkcije, umjesto da ga dereferencirate za čitanje ili pisanje podataka.

Da. Pokazivač funkcije može se postaviti na NULL što znači da nijedna funkcija nije dodijeljena. Pozivanje pokazivača funkcije s NULL vrijednosti uzrokuje nedefinirano ponašanje, stoga zaštitite poziv s if (ptr != NULL) prije pozivanja, posebno za opcionalne povratne pozive.

Da. Postavljanje pokazivača funkcija kao članova strukture omogućuje jednoj strukturi povezivanje podataka s povezanim operacijama, imitirajući objekte i tablice virtualnih metoda. Ovaj obrazac je osnova mnogih C biblioteka i upravljačkih programa uređaja, gdje svaka instanca odabire vlastite implementacije funkcija.

Da. Pomoćnik za umjetnu inteligenciju kodiranja može generirati deklaracije pokazivača funkcija, definicije tipova i potpise povratnih poziva iz jednostavnog prompta, objasniti zbunjujuću sintaksu i uočiti neusklađene parametre ili tipove povrata. Uvijek kompajlirajte i testirajte predloženi kod prije nego što mu vjerujete.

GitHub kopilot predlaže sintaksu pokazivača funkcija, dovršava tijela povratnih poziva iz komentara ili potpisa i predlaže qsort funkcije usporedbe. Otvorite relevantne zaglavne datoteke tako da se njihova dovršavanja podudaraju s vašim deklariranim prototipovima, a zatim pregledajte logiku.

Sažmite ovu objavu uz: