Funcții Indicatori în programarea C cu exemple

⚡ Rezumat inteligent

Pointerii de funcție în C stochează adresa codului executabil, permițând programelor să apeleze funcții indirect, să le transmită ca argumente, să construiască tabele de dispecerizare din tablouri și să implementeze apeluri inverse, cum ar fi rutina de comparație qsort, pentru design-uri flexibile și reutilizabile.

  • 🔗 Declaraţie: Un pointer de funcție folosește forma return_type(*nume)(argumente); parantezele din jurul numelui sunt obligatorii.
  • 🔄 Schimbați după adresă: Transmiterea adreselor variabile către parametrii pointerului permite unei funcții să modifice valorile originale ale apelantului, ca în exemplul swap.
  • 📦 Matrice: Un nume de matrice este deja un pointer, deci funcțiile pot primi o matrice sau pot returna un pointer către o matrice statică.
  • 🎛️ Tabele de expediere: O matrice de pointeri de funcții înlocuiește lanțurile long switch sau if prin indexarea directă la operația aleasă.
  • 🧊 indicatori void: Un parametru sau tip returnat void * acceptă orice tip, pe care funcția îl convertește înainte de utilizare.
  • 📞 Reapeluri: Transmiterea unui pointer de funcție ca argument alimentează apelurile inverse, cum ar fi funcția de comparație qsort.
  • 🤖 Asistență AI: Copilot-ul GitHub și asistenții AI schelează sintaxa indicatorilor de funcții, definițiile de tip și semnăturile de apel invers, apoi semnalează parametrii nepotriviți.

Pointeri de funcții în C

Pointerii oferă foarte multe posibilități funcțiilor „C” pentru care suntem limitați să returnăm o valoare. Cu parametrii pointer, funcțiile noastre pot procesa acum date reale, mai degrabă decât o copie a datelor.

Pentru a modifica valorile reale ale variabilelor, instrucțiunea de apelare transmite adrese parametrilor pointer într-o funcție.

Funcții Indicatori Exemplu

De exemplu, următorul program schimbă două valori din două:

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

ieșire:

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

Funcții Indicatori Exemplu

Programul schimbă valorile reale ale variabilelor, deoarece funcția le accesează folosind adresa indicatorul. Aici vom discuta despre procesul programului:

  1. Declarăm funcția responsabilă pentru swapping cele două valori variabile, care primește doi pointeri întregi ca parametri și returnează orice valoare atunci când este apelată.
  2. În funcția principală, declarăm și inițializam două variabile întregi ('m' și 'n'), apoi imprimăm valorile acestora.
  3. Apelăm funcția swap() prin trecerea adresei celor două variabile ca argumente folosind simbolul ampersand. După aceea, imprimăm noile valori schimbate ale variabilelor.
  4. Aici definim conținutul funcției swap() care ia ca parametri două adrese de variabile întregi și declarăm o variabilă întreagă temporară folosită ca a treia casetă de stocare pentru a salva una dintre variabilele de valoare care va fi pusă la a doua variabilă.
  5. Salvați conținutul primei variabile indicată de „a” în variabila temporară.
  6. Stocați a doua variabilă indicată de b în prima variabilă indicată de a.
  7. Actualizați a doua variabilă (indicată cu b) cu valoarea primei variabile salvate în variabila temporară.

Funcții cu parametrii de matrice

În C, nu putem transmite o matrice după valoare unei funcții. Întrucât, un nume de matrice este un pointer (adresă), deci trecem doar un nume de matrice unei funcții, ceea ce înseamnă să transmitem un pointer matricei.

De exemplu, luăm în considerare următorul 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);}

ieșire:

 Total summation is 471

Aici, vom explica codul programului cu detaliile acestuia

Funcții cu parametrii de matrice

  1. Declarăm și definim funcția add_array() care ia o adresă de matrice (pointer) cu numărul de elemente ca parametri și returnează suma totală acumulată a acestor elemente. Pointerul este folosit pentru a itera elementele matricei (folosind notația p[k]) și acumulăm suma într-o variabilă locală care va fi returnată după iterarea întregului tablou de elemente.
  2. Declaram si initializam un tablou intreg cu cinci elemente intregi. Tipărim suma totală trecând numele matricei (care acționează ca adresă) și dimensiunea matricei către add_array()numită funcție ca argumente.

Funcții care returnează o matrice

În C, putem returna un pointer către o matrice, ca în următorul program:

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

ieșire:

1
2
3
4
5

Și aici, vom discuta detaliile programului

Funcții care returnează o matrice

  1. Definim și declarăm o funcție care returnează o adresă matrice care conține o valoare întreagă și nu a luat niciun argument.
  2. Declaram un pointer intreg care primeste matricea completa construita dupa apelarea functiei si printam continutul acestuia prin iterarea intregii matrice de cinci elemente.

Observați că un pointer, nu o matrice, este definit pentru a stoca adresa matricei returnată de funcție. De asemenea, observați că atunci când o variabilă locală este returnată de la o funcție, trebuie să o declarăm ca statică în funcție.

Indicatori de funcție

După cum știm prin definiție că pointerii indică o adresă în orice locație de memorie, ei pot indica, de asemenea, la începutul codului executabil ca funcții din memorie.
Un pointer către funcție este declarat cu * , declarația generală a declarației sale este:

return_type (*function_name)(arguments)

Trebuie să vă amintiți că parantezele din jurul (*function_name) sunt importante deoarece fără ele, compilatorul va crede că function_name returnează un pointer de return_type.
După definirea indicatorului de funcție, trebuie să-l atribuim unei funcții. De exemplu, următorul program declară o funcție obișnuită, definește un pointer de funcție, atribuie pointerul funcției funcției obișnuite și apoi apelează funcția prin indicator:

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

ieșire:

Hi
Hi
Hi

Funcții Pointeri în C

  1. Definim și declarăm o funcție standard care afișează un text Hi k ori indicat de parametrul ori când funcția este apelată
  2. Definim o functie pointer (cu declaratia sa speciala) care preia un parametru intreg si nu returneaza nimic.
  3. Inițializam funcția de indicator cu Hi_function, ceea ce înseamnă că pointerul indică Hi_function().
  4. În loc de apelarea standard a funcției de către taping numele funcției cu argumente, apelăm doar funcția pointer transmițând numărul 3 ca argumente și gata!

Rețineți că numele funcției indică adresa de început a codului executabil ca un nume de matrice care indică primul său element. Prin urmare, instrucțiuni precum function_ptr = &Hi_function și (*funptr)(3) sunt corecte.
NOTĂ: Nu este important să introduceți operatorul de adresă & și operatorul indirect * în timpul atribuirii funcției și apelului de funcție.

Matrice de indicatori de funcție

O serie de indicatori de funcție poate juca un rol de comutare sau un rol de declarație if pentru a lua o decizie, ca în programul următor:

#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

Aici, discutăm detaliile programului:

Matrice de indicatori de funcție

  1. Declaram si definim patru funcții care primesc două argumente întregi și returnează o valoare întreagă. Aceste funcții adună, subtract, înmulțiți și împărțiți cele două argumente referitoare la funcția apelată de utilizator.
  2. Declarăm 4 numere întregi pentru a gestiona operanzi, tipul operației și, respectiv, rezultat. De asemenea, declarăm o matrice cu patru indicatori de funcție. Fiecare indicator de funcție al elementului de matrice ia doi parametri întregi și returnează o valoare întreagă.
  3. Atribuim și inițializam fiecare element de matrice cu funcția deja declarată. De exemplu, al treilea element care este al treilea indicator al funcției va indica funcția operației de multiplicare.
  4. Căutăm operanzi și tipul de operație de la utilizatorul tastat cu tastatura.
  5. Am apelat elementul de matrice adecvat (Function pointer) cu argumente și stocăm rezultatul generat de funcția corespunzătoare.

Instrucțiunea int (*ope[4])(int, int); definește matricea de pointeri de funcție. Fiecare element de matrice trebuie să aibă aceiași parametri și aceiași tip de returnare.
Rezultatul declarației = ope[choice](x, y); rulează funcția corespunzătoare în funcție de alegerea făcută de utilizator Cele două numere întregi introduse sunt argumentele transmise funcției.

Funcții Folosind pointeri void

Indicatorii de gol sunt utilizați în timpul declarațiilor de funcție. Folosim un tip de returnare void * permite returnarea oricărui tip. Dacă presupunem că parametrii noștri nu se modifică la trecerea la o funcție, o declarăm ca const.
De exemplu:

 void * cube (const void *);

Luați în considerare următorul 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

Aici vom discuta detaliile programului:

Funcții Folosind pointeri void

  1. Definim și declarăm o funcție care returnează o valoare întreagă și ia o adresă de variabilă neschimbabilă fără un tip de date specific. Calculăm valoarea de cub a variabilei de conținut (x) indicată de indicatorul num și, deoarece este un pointer void, trebuie să scriem cast-o într-un tip de date întreg folosind o notație specifică (* tip de date) pointer și revenim valoarea cubului.
  2. Declaram operandul si variabila rezultat. De asemenea, ne inițializam operandul cu valoarea „4”.
  3. Apelăm funcția cub prin transmiterea adresei operandului și ne ocupăm de valoarea returnată în variabila rezultat

Punctorii de funcție ca argumente

O altă modalitate de a exploata un indicator de funcție prin trecerea acestuia ca argument unei alte funcție numită uneori „funcție de apel invers”, deoarece funcția de recepție „o apelează înapoi”.
În fișierul antet stdlib.h, funcția Quicksort „qsort()” folosește această tehnică, care este un algoritm dedicat sortării unui tablou.

void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
  • void *base : void pointer către matrice.
  • size_t num : Numărul elementului matricei.
  • size_t width Dimensiunea elementului.
  • int (*compare (const void *, const void *) : indicatorul funcției compus din două argumente și returnează 0 când argumentele au aceeași valoare, <0 când arg1 vine înaintea arg2 și >0 când arg1 vine după arg2.

Următorul program sortează o matrice de numere întregi de la un număr mic la un număr mare folosind funcția 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

Aici vom discuta detaliile programului:

Punctorii de funcție ca argumente

  1. Definim funcția de comparare compusă din două argumente și returnează 0 când argumentele au aceeași valoare, <0 când arg1 vine înaintea arg2 și >0 când arg1 vine după arg2. Parametrii sunt un tip de pointeri void turnați la tipul de date matrice adecvat. (întreg)
  2. Definim și inițializam un tablou întreg. Dimensiunea matricei este stocată în o variabilă și dimensiunea fiecărui element de matrice este stocată în variabilă de lățime folosind sizeof() predefinit operator C.
  3. Apelăm funcția qsort și transmitem numele tabloului, dimensiunea, lățimea și funcția de comparație definite anterior de utilizator pentru a sorta tabloul nostru în ordine crescătoare. Comparația se va efectua prin luarea în fiecare iterație a două elemente ale tabloului până când întregul tablou va fi sortat.
  4. Tipărim elementele matricei pentru a ne asigura că matricea noastră este bine sortată prin iterarea întregii matrice folosind pentru bucla.

Întrebări frecvente

Da. Un indicator de funcție trebuie să corespundă exact tipului de returnare și listei de parametri a funcției țintă. Atribuirea unei funcții cu o semnătură diferită este un comportament nedefinit, chiar dacă se compilează, deoarece apelul ar trimite și ar citi argumentele incorect la momentul execuției.

typedef dă unui tip de pointer de funcție complex un nume scurt. Scrierea typedef int (*Operation)(int, int); vă permite să declarați Operaadăugare; în loc să se repete sintaxa completă a pointerului, tablourile, parametrii și membrii structurii devin mult mai ușor de citit.

Pointerii de funcții controlează apeluri inverse, tabele de salt sau dispecerizare, sisteme bazate pe pluginuri și evenimente, mașini de stare și rutine de bibliotecă precum qsort și bsearch. OperaSistemele de execuție și firmware-ul încorporat se bazează pe acestea pentru a selecta comportamentul în timpul execuției, fără lanțuri condiționale lungi.

Un indicator de date păstrează adresa unei variabile în memorie, în timp ce un indicator de funcție păstrează adresa de intrare a codului executabil. Apelați printr-un indicator de funcție pentru a rula funcția, în loc să o dereferențiați pentru a citi sau scrie date.

Da. Un pointer de funcție poate fi setat la NULL pentru a indica faptul că nu este atribuită nicio funcție. Apelarea unui pointer de funcție NULL provoacă un comportament nedefinit, așadar protejați apelul cu if (ptr != NULL) înainte de a-l invoca, în special pentru apeluri inverse opționale.

Da. Plasarea pointerilor de funcție ca membri struct permite unei structuri să grupeze datele cu operații corelate, imitând obiecte și tabele de metode virtuale. Acest model stă la baza multor biblioteci și drivere de dispozitiv C, unde fiecare instanță își selectează propriile implementări de funcții.

Da. Un asistent de codare bazat pe inteligență artificială poate genera declarații de pointeri de funcții, definiții de tip și semnături de apel invers dintr-un prompt în limbaj simplu, poate explica sintaxa confuză și poate identifica parametri sau tipuri de returnare nepotrivite. Compilați și testați întotdeauna codul sugerat înainte de a-l considera de încredere.

Copilotul GitHub sugerează sintaxa indicatorilor de funcții, completează corpurile de apel invers dintr-un comentariu sau o semnătură și propune funcții de comparare qsort. Deschideți fișierele antet relevante astfel încât completările acestora să corespundă cu prototipurile declarate, apoi examinați logica.

Rezumați această postare cu: