Funktsioonid Osutajad C-programmeerimisel koos näidetega
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
Programm vahetab tegelikke muutujate väärtusi, kuna funktsioon pääseb neile juurde aadressi abil pointer. Siin käsitleme programmi protsessi:
- Deklareerime kahe muutuja väärtuse vahetamise eest vastutava funktsiooni, mis võtab parameetritena kaks täisarvu osutit ja tagastab kutsumisel mis tahes väärtuse.
- Põhifunktsioonis deklareerime ja initsialiseerime kaks täisarvu muutujat ('m' ja 'n'), seejärel trükime nende väärtused vastavalt.
- Kutsume funktsiooni swap(), edastades kahe muutuja aadressid argumentidena, kasutades ampersandi sümbolit. Pärast seda trükime muutujate uued vahetatud väärtused.
- 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.
- Salvestage ajutise muutuja esimese muutuja sisu, millele osutab 'a'.
- Salvestage teine muutuja, millele osutab b, esimesse muutujasse, mida osutab a.
- 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
- 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.
- 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
- Defineerime ja deklareerime funktsiooni, mis tagastab täisarvulist väärtust sisaldava massiivi aadressi ja ei võtnud ühtegi argumenti.
- 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
- Defineerime ja deklareerime standardfunktsiooni, mis prindib funktsiooni kutsumisel Hi teksti k korda, mida näitavad parameetrid
- Määratleme kursorifunktsiooni (selle erideklaratsiooniga), mis võtab täisarvu parameetri ja ei tagasta midagi.
- Initsialiseerime oma kursori funktsiooni funktsiooniga Hi_, mis tähendab, et kursor osutab funktsioonile Hi_function().
- Selle asemel, et tavafunktsioon kutsuda funktsiooni nime argumentidega lindistades, kutsume välja ainult kursorifunktsiooni, edastades 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:
- Me deklareerime ja määratleme neli funktsioonid mis võtavad kaks täisarvu argumenti ja tagastavad täisarvu väärtuse. Need funktsioonid liidavad, lahutavad, korrutavad ja jagavad kaks argumenti selle kohta, millist funktsiooni kasutaja kutsub.
- 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.
- Määrame ja initsialiseerime iga massiivi elemendi juba deklareeritud funktsiooniga. Näiteks kolmas element, mis on kolmas funktsiooni osuti, osutab korrutamistoimingu funktsioonile.
- Otsime klaviatuuriga sisestatud kasutajalt operande ja toimingutüüpe.
- 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:
- 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.
- Deklareerime operandi ja tulemuse muutuja. Samuti initsialiseerime oma operandi väärtusega "4".
- 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:
- 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)
- 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.
- Me kutsume qsort funktsioon ja edastage massiivi nimi, suurus, laius ja võrdlusfunktsioon, mille kasutaja on eelnevalt määratlenud, et sorteerida meie massiivi kasvavas järjekorras. Võrdluseks võetakse igas iteratsioonis kaks massiivi elementi, kuni kogu massiiv on sorteeritud.
- Prindime massiivi elemendid, et olla kindel, et meie massiiv on hästi sorteeritud, itereerides kogu massiivi kasutades silmuse jaoks.