Funksjoner Pekere i C-programmering med eksempler

⚡ Smart oppsummering

Funksjonspekere i C lagrer adressen til kjørbar kode, slik at programmer kan kalle funksjoner indirekte, sende dem som argumenter, bygge forsendelsestabeller fra arrayer og implementere tilbakeringinger som qsort-sammenligningsrutinen for fleksible, gjenbrukbare design.

  • 🔗 Erklæring: En funksjonspeker bruker formen return_type(*name)(arguments); parentesene rundt navnet er obligatoriske.
  • 🔄 Bytt etter adresse: Å sende variable adresser til pekerparametere lar en funksjon endre de opprinnelige verdiene til den som ringer, som i bytteeksemplet.
  • 📦 Matriser: Et arraynavn er allerede en peker, så funksjoner kan motta en array eller returnere en peker til en statisk array.
  • 🎛️ Forsendelsestabeller: En matrise med funksjonspekere erstatter lange switch- eller if-kjeder ved å indeksere direkte til den valgte operasjonen.
  • 🧊 tomromspekere: En void *-parameter eller returtype aksepterer enhver type, som funksjonen bruker tilbake før bruk.
  • 📞 Tilbakeringinger: Å sende en funksjonspeker som et argument driver tilbakekall som qsort-sammenligningsfunksjonen.
  • 🤖 AI-hjelp: GitHub Copilot og AI-assistenter stillaser funksjonspekersyntaks, typedefiner og tilbakeringingssignaturer, og flagger deretter parametere som ikke samsvarer.

Funksjonspekere i C

Pekere gir store muligheter til 'C'-funksjoner som vi er begrenset til å returnere én verdi. Med pekerparametere kan funksjonene våre nå behandle faktiske data i stedet for en kopi av data.

For å endre de faktiske verdiene til variabler, sender den kallende setningen adresser til pekerparametere i en funksjon.

Funksjoner Pekere Eksempel

For eksempel bytter det neste programmet to verdier av to:

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

Utgang:

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

Funksjoner Pekere Eksempel

Programmet bytter de faktiske variabelverdiene fordi funksjonen får tilgang til dem ved å bruke adresse pekeren. Her vil vi diskutere programprosessen:

  1. Vi erklærer funksjonen som er ansvarlig for byttetping de to variabelverdiene, som tar to heltallspekere som parametere og returnerer en hvilken som helst verdi når den kalles.
  2. I hovedfunksjonen deklarerer og initialiserer vi to heltallsvariabler ('m' og 'n'), så skriver vi ut verdiene deres.
  3. Vi kaller swap()-funksjonen ved å sende adressen til de to variablene som argumenter ved å bruke og-tegnet. Etter det skriver vi ut de nye byttede verdiene til variabler.
  4. Her definerer vi swap()-funksjonen innhold som tar to heltallsvariabeladresser som parametere og erklærer en midlertidig heltallsvariabel brukt som en tredje lagringsboks for å lagre en av verdivariablene som vil bli satt til den andre variabelen.
  5. Lagre innholdet i den første variabelen pekt med 'a' i den midlertidige variabelen.
  6. Lagre den andre variabelen pekt med b i den første variabelen pekt med a.
  7. Oppdater den andre variabelen (pekt med b) med verdien av den første variabelen lagret i den midlertidige variabelen.

Fungerer med matriseparametere

I C kan vi ikke sende en matrise etter verdi til en funksjon. Mens et matrisenavn er en peker (adresse), så vi sender bare et matrisenavn til en funksjon som betyr å sende en peker til matrisen.

For eksempel vurderer vi følgende 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);}

Utgang:

 Total summation is 471

Her vil vi forklare programkoden med detaljer

Fungerer med matriseparametere

  1. Vi erklærer og definerer add_array()-funksjonen som tar en matriseadresse(peker) med elementnummeret som parametere og returnerer den totale akkumulerte summeringen av disse elementene. Pekeren brukes til å iterere array-elementene (ved å bruke p[k]-notasjonen), og vi akkumulerer summeringen i en lokal variabel som vil bli returnert etter iterasjon av hele element-arrayen.
  2. Vi erklærer og initialiserer en heltallsmatrise med fem heltallselementer. Vi skriver ut den totale summeringen ved å sende matrisenavnet (som fungerer som adresse) og matrisestørrelsen til add_array()kalt funksjon som argumenter.

Funksjoner som returnerer en matrise

I C kan vi returnere en peker til en matrise, som i følgende 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);}

Utgang:

1
2
3
4
5

Og her vil vi diskutere programdetaljene

Funksjoner som returnerer en matrise

  1. Vi definerer og deklarerer en funksjon som returnerer en matriseadresse som inneholder en heltallsverdi og som ikke tok noen argumenter.
  2. Vi erklærer en heltallspeker som mottar hele matrisen bygget etter at funksjonen er kalt, og vi skriver ut innholdet ved å iterere hele matrisen med fem elementer.

Legg merke til at en peker, ikke en matrise, er definert for å lagre matriseadressen som returneres av funksjonen. Legg også merke til at når en lokal variabel returneres fra en funksjon, må vi erklære den som statisk i funksjonen.

Funksjonspekere

Som vi per definisjon vet at pekere peker til en adresse på en hvilken som helst minneplassering, kan de også peke på i begynnelsen av kjørbar kode som funksjoner i minnet.
En peker til funksjon er deklarert med *, den generelle erklæringen i erklæringen er:

return_type (*function_name)(arguments)

Du må huske at parentesene rundt (*funksjonsnavn) er viktige fordi uten dem vil kompilatoren tro at funksjonsnavnet returnerer en peker av returtype.
Etter å ha definert funksjonspekeren, må vi tilordne den til en funksjon. For eksempel erklærer det neste programmet en ordinær funksjon, definerer en funksjonspeker, tildeler funksjonspekeren til den ordinære funksjonen og kaller deretter funksjonen gjennom pekeren:

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

Utgang:

Hi
Hi
Hi

Funksjoner Pekere i C

  1. Vi definerer og deklarerer en standardfunksjon som skriver ut en Hi-tekst k ganger angitt med parametertidene når funksjonen kalles
  2. Vi definerer en pekerfunksjon (med sin spesielle erklæring) som tar en heltallsparameter og ikke returnerer noe.
  3. Vi initialiserer pekerfunksjonen vår med Hi_function som betyr at pekeren peker til Hi_function().
  4. I stedet for standardfunksjonskallet fra taping funksjonsnavnet med argumenter, vi kaller bare pekerfunksjonen ved å sende tallet 3 som argumenter, og det er det!

Husk at funksjonsnavnet peker til startadressen til den kjørbare koden som et matrisenavn som peker til det første elementet. Derfor er instruksjoner som function_ptr = &Hi_function og (*funptr)(3) korrekte.
MERK: Det er ikke viktig å sette inn adresseoperatøren & og indirektionsoperatøren * under funksjonstildelingen og funksjonsanropet.

En rekke funksjonspekere

En rekke funksjonspekere kan spille en bryter eller en if-utsagnsrolle for å ta en beslutning, som i neste program:

#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

Her diskuterer vi programdetaljene:

En rekke funksjonspekere

  1. Vi erklærer og definerer fire funksjoner som tar to heltallsargumenter og returnerer en heltallsverdi. Disse funksjonene legger til, subtraherertract, multipliser og divider de to argumentene angående hvilken funksjon som kalles av brukeren.
  2. Vi erklærer 4 heltall for å håndtere henholdsvis operander, operasjonstype og resultat. Dessuten erklærer vi en matrise med fire funksjonspekere. Hver funksjonspeker til matriseelementet tar to heltallsparametere og returnerer en heltallsverdi.
  3. Vi tildeler og initialiserer hvert array-element med funksjonen som allerede er erklært. For eksempel vil det tredje elementet som er den tredje funksjonspekeren peke på multiplikasjonsoperasjonsfunksjonen.
  4. Vi søker operander og type operasjon fra brukeren som skrives inn med tastaturet.
  5. Vi kalte det passende array-elementet (Funksjonspeker) med argumenter, og vi lagrer resultatet generert av den aktuelle funksjonen.

Instruksjonen int (*ope[4])(int, int); definerer utvalget av funksjonspekere. Hvert matriseelement må ha samme parametere og returtype.
Utsagnsresultatet = ope[valg](x, y); kjører den aktuelle funksjonen i henhold til valget gjort av brukeren. De to angitte heltallene er argumentene som sendes til funksjonen.

Funksjoner Bruke void-pekere

Void-pekere brukes under funksjonsdeklarasjoner. Vi bruker en void * returtypetillatelser for å returnere alle typer. Hvis vi antar at parameterne våre ikke endres når de overføres til en funksjon, erklærer vi den som const.
For eksempel:

 void * cube (const void *);

Tenk på følgende 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;}

Resultat:

 4 cubed is 64

Her vil vi diskutere programdetaljene:

Funksjoner Bruke void-pekere

  1. Vi definerer og deklarerer en funksjon som returnerer en heltallsverdi og tar en adresse med uforanderlig variabel uten en spesifikk datatype. Vi beregner kubeverdien til innholdsvariabelen (x) pekt av num-pekeren, og siden det er en void-peker, må vi skrive cast den til en heltallsdatatype ved å bruke en spesifikk notasjon (* datatype)-peker, og vi returnerer kubeverdien.
  2. Vi erklærer operanden og resultatvariabelen. Vi initialiserer også operanden vår med verdien «4».
  3. Vi kaller kubefunksjonen ved å sende operandadressen, og vi håndterer returverdien i resultatvariabelen

Fungerer pekere som argumenter

En annen måte å utnytte en funksjonspeker ved å sende den som et argument til en annen funksjon som noen ganger kalles "tilbakeringingsfunksjon" fordi mottakerfunksjonen "kaller den tilbake."
I headerfilen stdlib.h bruker Quicksort "qsort()"-funksjonen denne teknikken som er en algoritme dedikert til å sortere en matrise.

void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
  • void *base: void-peker til matrisen.
  • size_t num : Matriseelementnummeret.
  • size_t width Elementstørrelsen.
  • int (*compare (const void *, const void *) : funksjonspeker sammensatt av to argumenter og returnerer 0 når argumentene har samme verdi, <0 når arg1 kommer før arg2, og >0 når arg1 kommer etter arg2.

Følgende program sorterer en heltallsmatrise fra lite til stort tall ved å bruke qsort()-funksjonen:

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

Resultat:

 13 14 48 50 52

Her vil vi diskutere programdetaljene:

Fungerer pekere som argumenter

  1. Vi definerer sammenligningsfunksjonen sammensatt av to argumenter og returnerer 0 når argumentene har samme verdi, <0 når arg1 kommer før arg2, og >0 når arg1 kommer etter arg2. Parametrene er en void pointer-type som er castet til den aktuelle matrisedatatypen (heltall)
  2. Vi definerer og initialiserer en heltallsmatrise Matrisestørrelsen er lagret i NUM variabel og størrelsen på hvert array-element lagres i breddevariabel ved å bruke sizeof() forhåndsdefinert C-operatør.
  3. Vi kaller qsort-funksjonen og sender arraynavnet, størrelsen, bredden og sammenligningsfunksjonen som brukeren har definert tidligere, for å sortere arrayet vårt i stigende rekkefølge. Sammenligningen utføres ved å ta inn to arrayelementer i hver iterasjon til hele arrayet er sortert.
  4. Vi skriver ut array-elementene for å være sikre på at arrayet vårt er godt sortert ved å iterere hele arrayet ved å bruke for sløyfe.

Spørsmål og svar

Ja. En funksjonspeker må samsvare nøyaktig med målfunksjonens returtype og parameterliste. Å tilordne en funksjon med en annen signatur er udefinert oppførsel, selv om den kompilerer, fordi kallet ville pushe og lese argumenter feil under kjøretid.

typedef gir en kompleks funksjonspekertype et kort navn. Skriver typedef int (*Operasjon)(int, int); lar deg deklarere Operation add; i stedet for å gjenta hele pekersyntaksen, noe som gjør arrayer, parametere og strukturmedlemmer mye enklere å lese.

Funksjonspekere driver tilbakeringinger, hopp- eller forsendelsestabeller, plugin- og hendelsesdrevne systemer, tilstandsmaskiner og bibliotekrutiner som qsort og bsearch. Operating-systemer og innebygd fastvare er avhengige av dem for å velge atferd under kjøretid uten lange betingede kjeder.

En datapeker inneholder adressen til en variabel i minnet, mens en funksjonspeker inneholder inngangsadressen til kjørbar kode. Du kaller gjennom en funksjonspeker for å kjøre funksjonen, i stedet for å dereferere den for å lese eller skrive data.

Ja. En funksjonspeker kan settes til NULL, slik at ingen funksjon er tilordnet. Å kalle en NULL-funksjonspeker forårsaker udefinert oppførsel, så beskytt kallet med if (ptr != NULL) før du kaller det, spesielt for valgfrie tilbakeringinger.

Ja. Ved å plassere funksjonspekere som strukturmedlemmer kan en struktur pakke data med relaterte operasjoner, imitere objekter og virtuelle metodetabeller. Dette mønsteret ligger til grunn for mange C-biblioteker og enhetsdrivere, der hver instans velger sine egne funksjonsimplementeringer.

Ja. En AI-kodingsassistent kan generere funksjonspekerdeklarasjoner, typedefiner og tilbakeringingssignaturer fra en ledetekst i et enkelt språk, forklare forvirrende syntaks og oppdage parametere eller returtyper som ikke samsvarer. Kompiler og test alltid den foreslåtte koden før du stoler på den.

GitHub Copilot foreslår syntaks for funksjonspekere, fullfører tilbakekallingselementer fra en kommentar eller signatur, og foreslår qsort-sammenligningsfunksjoner. Åpne de relevante headerfilene slik at fullføringene samsvarer med de deklarerte prototypene dine, og gjennomgå deretter logikken.

Oppsummer dette innlegget med: