Funktsioonid Osutajad C-programmeerimisel koos näidetega

⚡ Nutikas kokkuvõte

C-keele funktsiooniviited salvestavad käivitatava koodi aadressi, võimaldades programmidel funktsioone kaudselt kutsuda, neid argumentidena edastada, massiividest väljaheitetabeleid luua ja paindlike ja korduvkasutatavate disainide jaoks tagasihelistusfunktsioone, näiteks qsort võrdlusrutiini, rakendada.

  • 🔗 Deklaratsioon: Funktsiooni pointer kasutab vormi return_type(*nimi)(arguments); sulud nime ümber on kohustuslikud.
  • 🔄 Vaheta aadressi järgi: Muutujate aadresside edastamine pointeri parameetritele võimaldab funktsioonil muuta kutsuja algseid väärtusi, nagu swap-näites.
  • 📦 Massiivid: Massiivi nimi on juba pointer, seega saavad funktsioonid massiivi vastu võtta või pointeri staatilisse massiivi tagastada.
  • 🎛️ Lähetustabelid: Funktsiooniviidete massiiv asendab pikki lülitus- või if-ahelaid, indekseerides otse valitud operatsioonile.
  • 🧊 tühised osutid: Void * parameeter või tagastustüüp aktsepteerib mis tahes tüüpi, mille funktsioon enne kasutamist tagasi teisendab.
  • 📞 Tagasihelistamised: Funktsiooni pointeri argumendina edastamine võimaldab kasutada tagasihelistusfunktsioone, näiteks qsort võrdlusfunktsiooni.
  • 🤖 AI abi: GitHub Copiloti ja tehisintellekti assistendid loovad funktsiooni pointeri süntaksi, tüübimäärangud ja tagasihelistussignatuurid ning märgistavad seejärel mittevastavad parameetrid.

Funktsioonide osutid C-s

Osutajad annavad palju võimalusi 'C' funktsioonidele, mille puhul piirdume ühe väärtuse tagastamisega. Kursori parameetritega saavad meie funktsioonid nüüd töödelda tegelikke andmeid, mitte andmekoopiaid .

Muutujate tegelike väärtuste muutmiseks edastab kutsuv lause aadressid funktsiooni kursori parameetritele.

Funktsioonid Osutajad Näide

Näiteks vahetab järgmine programm kaks väärtust kahest:

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äljund:

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

Funktsioonid Osutajad Näide

Programm vahetab tegelikke muutujate väärtusi, kuna funktsioon pääseb neile juurde aadressi abil pointer. Siin käsitleme programmi protsessi:

  1. Me deklareerime vahetuse eest vastutava funktsiooniping kahe muutuja väärtused, mis võtab parameetriteks kaks täisarvulist pointerit ja tagastab kutsumisel mis tahes väärtuse.
  2. Põhifunktsioonis deklareerime ja initsialiseerime kaks täisarvu muutujat ('m' ja 'n'), seejärel trükime nende väärtused vastavalt.
  3. Kutsume funktsiooni swap(), edastades kahe muutuja aadressid argumentidena, kasutades ampersandi sümbolit. Pärast seda trükime muutujate uued vahetatud väärtused.
  4. Siin määratleme funktsiooni swap() sisu, mis võtab parameetritena kaks täisarvulise muutuja aadressi ja deklareerib ajutise täisarvu muutuja, mida kasutatakse kolmanda salvestuskastina, et salvestada üks väärtusmuutujatest, mis pannakse teisele muutujale.
  5. Salvestage ajutise muutuja esimese muutuja sisu, millele osutab 'a'.
  6. Salvestage teine ​​muutuja, millele osutab b, esimesse muutujasse, mida osutab a.
  7. Värskendage teist muutujat (osutatud b-ga) ajutisse muutujasse salvestatud esimese muutuja väärtusega.

Funktsioonid massiivi parameetritega

C-s ei saa me massiivi väärtuse järgi funktsioonile edasi anda. Arvestades, et massiivi nimi on osuti (aadress), edastame massiivi nime lihtsalt funktsioonile, mis tähendab massiivile kursori edastamist.

Näiteks kaalume järgmist programmi:

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äljund:

 Total summation is 471

Siin selgitame programmi koodi ja selle üksikasju

Funktsioonid massiivi parameetritega

  1. Deklareerime ja määratleme funktsiooni add_array(), mis võtab parameetritena massiivi aadressi (pointer) koos selle elementide numbriga ja tagastab nende elementide kogusumma. Kursorit kasutatakse massiivi elementide itereerimiseks (kasutades tähistust p[k]) ja me kogume summeerimise kohalikus muutujas, mis tagastatakse pärast kogu elemendimassiivi itereerimist.
  2. Deklareerime ja initsialiseerime viie täisarvulise elemendiga täisarvu massiivi. Trükime kokku summeerimise, edastades massiivi nime (mis toimib aadressina) ja massiivi suuruse add_massiiv()nimetatakse funktsiooniks argumentideks.

Funktsioonid, mis tagastavad massiivi

C-s saame tagastada osuti massiivile, nagu järgmises programmis:

#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äljund:

1
2
3
4
5

Ja siin arutame programmi üksikasju

Funktsioonid, mis tagastavad massiivi

  1. Defineerime ja deklareerime funktsiooni, mis tagastab täisarvulist väärtust sisaldava massiivi aadressi ja ei võtnud ühtegi argumenti.
  2. Deklareerime täisarvu osuti, mis võtab vastu pärast funktsiooni kutsumist kogu massiivi, ja prindime selle sisu, itereerides kogu viie elemendi massiivi.

Pange tähele, et funktsiooni tagastatud massiivi aadressi salvestamiseks on määratletud osuti, mitte massiiv. Pange tähele ka seda, et kui funktsioonist tagastatakse kohalik muutuja, peame selle funktsioonis staatiliseks deklareerima.

Funktsiooniosutajad

Nagu me definitsiooni järgi teame, et osutid osutavad mis tahes mälukohas olevale aadressile, võivad nad osutada ka käivitatava koodi alguses olevale funktsioonile mälus.
Funktsiooni osuti deklareeritakse tähega *, selle deklaratsiooni üldlause on:

return_type (*function_name)(arguments)

Peate meeles pidama, et sulud (*funktsiooni_nimi) ümber on olulised, sest ilma nendeta arvab kompilaator, et funktsiooni_nimi tagastab osuti, mille väärtus on return_type.
Pärast funktsiooni osuti määratlemist peame selle funktsioonile määrama. Näiteks järgmine programm deklareerib tavalise funktsiooni, määrab funktsiooni osuti, määrab funktsiooni osuti tavalisele funktsioonile ja pärast seda kutsub funktsiooni kursori kaudu:

#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äljund:

Hi
Hi
Hi

Funktsioonid Osutajad C-s

  1. Defineerime ja deklareerime standardfunktsiooni, mis prindib funktsiooni kutsumisel Hi teksti k korda, mida näitavad parameetrid
  2. Määratleme kursorifunktsiooni (selle erideklaratsiooniga), mis võtab täisarvu parameetri ja ei tagasta midagi.
  3. Initsialiseerime oma kursori funktsiooni funktsiooniga Hi_, mis tähendab, et kursor osutab funktsioonile Hi_function().
  4. Standardfunktsiooni kutsumise asemel, mida teeb taping funktsiooni nimi argumentidega, kutsume välja ainult pointerfunktsiooni, andes argumentidena arvu 3 ja ongi kõik!

Pidage meeles, et funktsiooni nimi osutab käivitatava koodi algusaadressile nagu massiivi nimi, mis osutab selle esimesele elemendile. Seetõttu on sellised juhised nagu function_ptr = &Hi_function ja (*funptr)(3) õiged.
MÄRKUS. Funktsiooni määramise ja funktsiooni kutsumise ajal ei ole oluline sisestada aadressioperaatorit & ja kaudset operaatorit *.

Funktsiooniosutite massiiv

Funktsiooniosutite massiiv võib otsuse tegemisel mängida lülitit või if-lause rolli, nagu järgmises programmis:

#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

Siin käsitleme programmi üksikasju:

Funktsiooniosutite massiiv

  1. Me deklareerime ja määratleme neli funktsioonid mis võtavad vastu kaks täisarvulist argumenti ja tagastavad täisarvulise väärtuse. Need funktsioonid liidavad, sub-idtract, korrutage ja jagage kaks argumenti, mis määravad, millist funktsiooni kasutaja kutsub.
  2. Deklareerime 4 täisarvu, et käsitleda vastavalt operande, operatsiooni tüüpi ja tulemust. Samuti deklareerime nelja funktsioonikursori massiivi. Iga massiivielemendi funktsiooni osuti võtab kaks täisarvu parameetrit ja tagastab täisarvu väärtuse.
  3. Määrame ja initsialiseerime iga massiivi elemendi juba deklareeritud funktsiooniga. Näiteks kolmas element, mis on kolmas funktsiooni osuti, osutab korrutamistoimingu funktsioonile.
  4. Otsime klaviatuuriga sisestatud kasutajalt operande ja toimingutüüpe.
  5. Kutsusime argumentidega välja sobiva massiivielemendi (Function pointer) ja salvestame vastava funktsiooni genereeritud tulemuse.

Käsk int (*ope[4])(int, int); määratleb funktsiooniosutajate massiivi. Igal massiivi elemendil peavad olema samad parameetrid ja tagastustüüp.
Lause tulemus = ope[valik](x, y); käivitab vastava funktsiooni vastavalt kasutaja tehtud valikule. Kaks sisestatud täisarvu on funktsioonile edastatavad argumendid.

Funktsioonid tühiste osutite kasutamine

Funktsioonide deklareerimisel kasutatakse tühiseid viiteid. Kasutame tühi * tagastustüüpi, mis lubab tagastada mis tahes tüüpi. Kui eeldame, et meie parameetrid funktsioonile üleminekul ei muutu, deklareerime selle kui const.
Näiteks:

 void * cube (const void *);

Kaaluge järgmist programmi:

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

Tulemus:

 4 cubed is 64

Siin arutame programmi üksikasju:

Funktsioonid tühiste osutite kasutamine

  1. Defineerime ja deklareerime funktsiooni, mis tagastab täisarvu väärtuse ja võtab muutumatu muutuja aadressi ilma konkreetse andmetüübita. Arvutame num osutiga näidatud sisumuutuja (x) kuubiväärtuse ja kuna see on tühine osuti, peame tippima selle täisarvulise andmetüübina, kasutades konkreetset tähistus (* andmetüüp) kursorit ja tagastame kuubi väärtus.
  2. Deklareerime operandi ja tulemuse muutuja. Samuti initsialiseerime oma operandi väärtusega "4".
  3. Kutsume kuubifunktsiooni, edastades operandi aadressi, ja käsitleme tagastatavat väärtust tulemusmuutujas

Funktsiooni Osutajad kui argumentid

Veel üks võimalus funktsiooni osuti kasutamiseks, edastades selle argumendina teisele funktsioonile, mida mõnikord nimetatakse "tagasikutsumise funktsiooniks", kuna vastuvõttev funktsioon "kutsub selle tagasi".
Päisefailis stdlib.h kasutab funktsioon Quicksort “qsort()” seda tehnikat, mis on massiivi sortimiseks mõeldud algoritm.

void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
  • void *base : tühine osuti massiivile.
  • size_t num : massiivi elemendi number.
  • size_t laius Elemendi suurus.
  • int (*võrdle (const void *, const void *) : funktsiooni osuti, mis koosneb kahest argumendist ja tagastab 0, kui argumentidel on sama väärtus, <0, kui arg1 tuleb enne arg2 ja >0, kui arg1 tuleb pärast arg2.

Järgmine programm sorteerib täisarvude massiivi väikesest suureni, kasutades funktsiooni 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;}

Tulemus:

 13 14 48 50 52

Siin arutame programmi üksikasju:

Funktsiooni Osutajad kui argumentid

  1. Defineerime võrdlusfunktsiooni, mis koosneb kahest argumendist ja tagastab 0, kui argumentidel on sama väärtus, <0, kui arg1 tuleb enne arg2 ja >0, kui arg1 tuleb pärast arg2. Parameetrid on tühikute viitetüüp, mis on kantud sobivasse massiivi andmetüüpi (täisarv)
  2. Määratleme ja initsialiseerime täisarvu massiivi Massiivi suurus salvestatakse num muutuja ja iga massiivi elemendi suurus salvestatakse laiuse muutujasse, kasutades eelnevalt määratud sizeof()-i C operaator.
  3. Kutsume välja qsort funktsiooni ja edastame kasutaja poolt eelnevalt defineeritud massiivi nime, suuruse, laiuse ja võrdlusfunktsiooni, et sortida massiiv kasvavas järjekorras. Võrdlus tehakse nii, et igas iteratsioonis võetakse kaks massiivi elementi, kuni kogu massiiv on sorteeritud.
  4. Prindime massiivi elemendid, et olla kindel, et meie massiiv on hästi sorteeritud, itereerides kogu massiivi kasutades silmuse jaoks.

KKK

Jah. Funktsiooni pointer peab täpselt vastama sihtfunktsiooni tagastustüübile ja parameetrite loendile. Funktsioonile erineva signatuuri määramine on defineerimata käitumine isegi siis, kui see kompileerub, sest kutse edastaks ja loeks käitusajal argumente valesti.

typedef annab keeruka funktsiooni pointeri tüübile lühikese nime. typedef int (* kirjutamineOperation)(int, int); võimaldab teil deklareerida Operation add; kogu pointeri süntaksi kordamise asemel muudab massiivid, parameetrid ja struktuuri liikmed palju lihtsamini loetavaks.

Funktsioonide pointerid juhivad tagasihelistusi, hüppe- või lähetustabelit, pluginate ja sündmustepõhiseid süsteeme, olekumasinaid ja teekirutiine nagu qsort ja bsearch. OperaKeemilised süsteemid ja manustatud püsivara toetuvad neile käitumise valimiseks käitusajal ilma pikkade tingimuslike ahelateta.

Andmeviide hoiab mälus oleva muutuja aadressi, funktsiooniviide aga käivitatava koodi sisestusaadressi. Funktsiooni käivitamiseks kutsutakse funktsiooniviidet, selle asemel et andmete lugemiseks või kirjutamiseks sellele viidet eemaldada.

Jah. Funktsiooni pointeri saab määrata väärtusele NULL, mis tähendab, et funktsiooni pole määratud. NULL-funktsiooni pointeri kutsumine põhjustab määratlemata käitumist, seega kaitske seda enne selle käivitamist atribuudiga if (ptr != NULL), eriti valikuliste tagasihelistusfunktsioonide puhul.

Jah. Funktsiooniviidete paigutamine struktuuri liikmeteks võimaldab ühel struktuuril siduda andmeid seotud toimingutega, imiteerides objekte ja virtuaalseid meetoditabeleid. See muster on paljude C-teekide ja seadmedraiverite aluseks, kus iga eksemplar valib oma funktsioonide implementatsioonid.

Jah. Tehisintellektiga kodeerimisassistent saab lihtsas keeles käsurealt genereerida funktsiooni pointeri deklaratsioone, tüübimääranguid ja tagasihelistussignatuure, selgitada segast süntaksit ning leida sobimatuid parameetreid või tagastustüüpe. Enne soovitatud koodi usaldamist kompileerige ja testige seda alati.

GitHubi koopia Soovitab funktsiooni pointeri süntaksit, täiendab kommentaari või signatuuri abil tagasihelistusteksti sisu ja pakub välja qsort võrdlusfunktsioone. Avage vastavad päisefailid nii, et nende täiendamised vastaksid teie deklareeritud prototüüpidele, ja seejärel vaadake loogika üle.

Võta see postitus kokku järgmiselt: