1 / 64

Procedur álne programovanie: 5 . prednáška

Procedur álne programovanie: 5 . prednáška. Gabriela Kosková. Obsah. opakovanie (2 p ríklady) ukazovatele príklady (3). #include<stdio.h> #define PI 3.14 #define obvod_m(r) (2 * PI * (r)) #define obsah_m(r) (PI * (r) * (r)) double obvod_f(double r) { return 2 * PI * r; }

ayanna
Download Presentation

Procedur álne programovanie: 5 . prednáška

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Procedurálne programovanie:5. prednáška Gabriela Kosková

  2. Obsah • opakovanie (2 príklady) • ukazovatele • príklady (3)

  3. #include<stdio.h> #define PI 3.14 #define obvod_m(r) (2 * PI * (r)) #define obsah_m(r) (PI * (r) * (r)) double obvod_f(double r) { return 2 * PI * r; } double obsah_f(double r) { return PI * r * r; } Príklad 1 Program vypočíta obvod a obsah kruhu pomocou makier a funkcií int main() { double polomer; printf("Zadajte polomer kruhu: "); scanf("%lf", &polomer); printf("\nMakro: obvod: %.2f, obsah: %.2f\n", obvod_m(polomer), obsah_m(polomer)); printf("Funkcie: obvod: %.2f, obsah: %.2f\n", obvod_f(polomer), obsah_f(polomer)); return 0; }

  4. Príklad 2 Program vypíše obsah súboru naopak.

  5. void vypis(FILE *f) { int c; if ((c=getc(f)) != EOF) { vypis(f); putchar(c); } } #include<stdio.h> int main() { int c; FILE *f; if ((f = fopen("subor.txt", "r")) == NULL) { printf("Nepodarilo sa otvorit subor.\n"); return 1; } vypis(f); if (fclose(f) == EOF) { printf("Nepodarilo sa zatvorit subor.\n"); return 1; } return 0; }

  6. zásobník Použitá rekurzia void vypis(FILE *f) { int c; if ((c=getc(f)) != EOF) { vypis(f); putchar(c); } } volanie vypis c: 'j' volanie vypis c: 'o' volanie vypis c: 'h' volanie vypis c: 'a' volanie vypis c: EOF a h výpis c: 'a' joha obsah súboru: výpis c: 'h' o výpis c: 'o' j výpis c: 'j' h výstup programu: a o j

  7. Procedurálne programovanie: Základy práce s ukazovateľmi

  8. Čo sú to ukazovatele = pointery, smerníky • ukazovateľ • je premenná • jeho hodnota je adresa v pamäti • analógia: v texte článku nie je informácia priamo uvedená, ale je tam odkaz na nejaký iný článok, kde sa informácia nachádza

  9. Príklad ukazovateľa pamäť • ukazovateľ p: • zapísaný na adrese 73 • jeho hodnota je 30 a vyjadruje adresu, kde je uložená skutočná hodnota • na adrese 30 v pamäti je hodnota 25, ktorá sa použije napr. pri výpočtoch • p: ukazovateľ • *p: hodnota, kam ukazuje *p: 30 25 p: 73 30

  10. Ako poznáme ukazovateľ • ukazovateľ je definovaný pomocou * • int i- „klasická“ celočíselná premenná • int *p_i - ukazovateľ na celočíselnú premennú • definícia ukazovateľa: int i; int *p_i; int i, *p_i; je ekvivalentné

  11. int i, p_i; ... *p_i = i; 7 7 Čo urobí *p_i = i; pamäť • ukazovateľ p_i • p_i == 30 • *p_i== 25 7 i: 28 *p_i: 30 25 • obsah pamäte, na ktorú ukazuje p_i sa prepíše hodnotou premennej i • p_i ukazuje na to isté miesto v pamäti treba inicializovať p_i, napr. alokovať pamäť pre p_i p_i: 73 30

  12. int i, *p_i = &i; je ekvivalentné Ako získame adresu premennej • pomocou referenčného operátora & • int i- „klasická“ celočíselná premenná • &i – adresa premennej • definícia ukazovateľa: definícia ukazovateľaa súčasne inicializácia int i, *p_i; p_i = &i;

  13. Čo urobí p_i = &i; pamäť • ukazovateľ p_i • p_i == 30 • *p_i == 25 int i, *p_i; p_i = &i; 7 i: 28 *p_i: 30 25 • hodnota p_i (adresa, kam p_i ukazuje) sa prepíše adresou premennej i • hodnota *p_i je tá istá ako hodnota i p_i: 73 30

  14. Čo urobí p_i = &i; pamäť • ukazovateľ p_i • p_i == 30 • *p_i == 25 int i, *p_i; p_i = &i; 28 7 i: 28 7 *p_i: 30 25 • hodnota p_i (adresa, kam p_i ukazuje) sa prepíše adresou premennej i • hodnota *p_i je tá istá ako hodnota i p_i: 73 28 30

  15. Príklady - správne p_i = &i; - chyba: (i + 3) nie je premenná p_i = &(i + 3); - chyba: konštanta nemá adresu p_i = &15; - chyba: priraďovanie absolútnej adresy p_i = 15; - chyba: priraďovanie adresy i = p_i; - chyba: priraďovanie adresy i = & p_i; -správne, ak p_i bol inicializovaný *p_i = 4;

  16. Výpis adresy (pri ladení) • špecifikácia formátu (v printf()): %p int i, *p_i; p_i = &i; printf(„Adresa i: %p, hodnota p_i: %p\n”, &i, p_i);

  17. Keď ukazovateľ neukazuje nikam • Nulový ukazovateľ: NULL • NULL - symbolická konštanta definovaná v stdio.h: • #define NULL 0 • #define NULL ((void *) 0) • Je možné priradiť ho ukazovateľom na ľubovoľný typ if (p_i == NULL) ...

  18. Konverzia ukazovateľov • Vyhnúť sa jej! • Ak sa nedá vyhnúť – explicitne pretypovávať int *p_i; char *p_c; p_c = p_i; p_c = (char *)p_i; nevhodné vhodnejšie

  19. Funkcie: volanie odkazom • V C nie je volanie odkazom, funkcie sa volajú len hodnotou • Vo funkcii vzniká kópia argumentu funkcie (lokálna premenná), ktorá zaniká s ukončením funkcie • Preto sa funkcia nevolá s premennou, ktorú chceme meniť, ale s jej adresou

  20. spustenie programu, volanie main() vytvorí sa kópia volanie A() spustenie A(3) koniec A() návrat do main() koniec programu, main() Parametre funkcií - volanie hodnotou: int A(int i) dátová oblasť 3 3 4 zásobník

  21. spustenie programu, volanie main() adresa: 15 volanie A() spustenie A(15) koniec A() návrat do main() koniec programu, main() Parametre funkcií - volanie odkazom : int A(int *i) dátová oblasť 4 3 adresa premennej 15 zásobník

  22. p_x p_y pom: Príklad funkcie: výmena premenných • volanie funkcie:vymen(&i, &j) void vymen(int *p_x, int *p_y) { int pom; pom = *p_x; *p_x = *p_y; *p_y = pom; } i: 5 j: 7 7 5 5

  23. Príklad funkcie: výmena premenných • volanie funkcie:vymen(&i, &j) chyba: vymieňa obsah adries, daných obsahom i, j: vymieňa hodnoty na adresách 5 a 7 vymen(i,j); chyba: vymieňa adresy adries z obsahu i, j: z adries 5 a 7 sa zoberú hodnoty a tie sa použijú ako adresy vymen(*i, *j);

  24. Ukazovateľ na typ void • void: prázdny typ (napr. funkcia, ktorá nevracia typ) • void *p_void; • neukazuje na žiaden kokrétny typ • generický pointer: ukazovateľ na ľubovoľný typ (nezabudnúť na pretypovanie!)

  25. Príklad ukazovateľa na typ void • pri priraďovaní je potrebné uviesť typ int i; float f; void *p_void = &i; *(int *) p_void = 2; p_void = &f; *(float *) p_void = 3.5; p_void ukazuje na i nastavenie i na 2 p_void ukazuje na f nastavenie f na 3.5

  26. Ukazovateľ na typ void: parameter funkcie pre int * pre char * char c = 'a', *p_c = &c, d = 'b', *p_d = &d; vymen((void **) &p_c, (void **) &p_d); int i = 1, *p_i = &i, j = 2, *p_j = &j; vymen((void **) &p_i, (void **) &p_j); funkcia na výmenu dvoch ukazovateľov void vymen(void**p_x, void**p_y) { void *p; p = *p_x; *p_x = *p_y; *p_y = p; }

  27. funkcia vracajúca ukazovateľ ukazovateľ na funkciu double *p_fd(); double (*p_fd)(); double scitaj(double x, double y) p_fd = scitaj; p_fd má adresu funkcie scitaj() Ukazovatele na funkcie • Funkcia môže vrátiť ukazovateľ na typ: • FILE *fopen(...) vracia smerník na typ FILE • Definovanie premennej ako ukazovateľ na funkciu: napr. double (*p_fd)(); to isté ako double *p_fd; double (*p_fd);

  28. Príklad ukazovateľa na funkciu funkcia na výpočet hodnôt polynómov (napr. x2+ 3, x + 8) pre zadanú hornú, dolnú hranicu a krok - najprv pomocné funkcie pre polynómy double pol1(double x) { return (x * x +3); } double pol2(double x) { return (x + 8); }

  29. void vypis(double d, double h, double k, double (*p_f)()) { double x; for(x=d; x<=h; x+=k) printf("%lf, %lf\n", x, (*p_f)(x)); } volanie vo funkcii main(): vypis(-1.0, 1.0, 0.1, pol1); vypis(-2.0, 2.0, 0.05, pol2); Príklad ukazovateľa na funkciu funkcia vypis() na vypísanie tabuľky

  30. Príklady definícií -i je typu int int i; -y je ukazovateľ na typ float float *y; -z je funkcia vracajúca ukazovateľ na double double *z(); - ukazovateľ na funkciu vracajúcu int int (*v)(); - ukazovateľ na funkciu vracajúcu ukazovateľ na int int *(*v)();

  31. Ako čítať zložitejšie definície • Nájdeme identifikátor, od neho čítame doprava • pokým nenarazíme na samotnú pravú zátvorku ")". Vraciame sa k zodpovedajúcej ľavej zátvorke. Potom pokračujeme doprava (preskakujeme prečítané) • Ak narazíme na ";" , vraciame sa na najľavejšie spracované miesto v čítame doľava

  32. v v) ) () 1. Nájdeme identifikátor: v, čítame doprava • Nájdeme ), k nej zodpovedajúcu (, od nej čítame • doprava: * 3. Doprava, preskakujeme prečítané, po ), k nej ( 4. Doprava, preskakujeme prečítané, po ;, doľava Príklad: čítanie definície int *(*v)(); pointer na funkciu vracajúcu - v je (*v) int * * int *(*v)(); pointer na int

  33. Definícia s využitím typedef • Operátor typedef • vytvára nový typ • najmä na definovanie zložitejších typov typedef float *P_FLOAT; P_FLOAT je ukazovateľ na typ float

  34. int *p_i, **p_p_i;  typedef int *P_INT; typedef P_INT *P_P_INT; P_INT p_i; P_P_INT p_p_i;  typedef double (*P_FD)(); Príklady použitia typedef p_i– ukazvateľ na int p_p_i – ukazvateľ na ukazovateľ na int je ekvivalentné ukazovateľ na funkciu vracajúcu double

  35. Ukazovateľová aritmetika • S ukazovateľmi sa dajú robiť niektoré aritmetické operácie: • Súčet ukazovateľa a celého čísla • Rozdiel ukazovateľa a celého čísla • Porovnávanie ukazovateľov rovnakého typu • Rozdiel dvoch ukazovateľov rovnakého typu • Majú zmysel len v rámci bloku dynamicky vytvorenej pamäte (POLIA) • Ostatné operácie nedávajú zmysel

  36. int i, *p_i; i = sizeof(p_i); int i, *p_i; i = sizeof(*p_i); Operátor sizeof • Na vysvetlenie aritmetických operácií s ukazovateľmi potrebujeme operátor sizeof(): • zistí veľkosť dátového typu v Bytoch • vyhodnotí sa v čase prekladu (nezdržuje beh) počet Bytov potrebných na uloženie ukazovateľa na int – nevyužíva sa počet Bytov potrebných na uloženie typu int – využíva sa často

  37. (n=3) p1: 30 1 2 32 3 34 p2 = (int*) p1 + sizeof(*p1)*n; 4 36 p2 = 30 + 2 * 3 = 36 Súčet ukazovateľa a celého čísla intn, *p1, *p2; ... p2 = p1+ n; sizeof(*p1)==2 p2:

  38. p_c + 1 == p_i + 1 == p_f + 1 == Súčet ukazovateľa a celého čísla - príklady char *p_c = 10; int *p_i = 20; float *p_f = 30; Vieme: sizeof(char) == 1 sizeof(int) == 2 sizeof(float) == 4 11 Potom platí: 22 34

  39. (n=3) 30 1 2 32 3 34 p1 = (int*) p2 - sizeof(*p2)*n; 4 p2: 36 p2 = 36 - 2 * 3 = 30 Rozdiel ukazovateľa a celého čísla intn, *p1, *p2; ... p1 = p2- n; sizeof(*p2)==2 p1:

  40. Porovnávanie ukazovateľov • operátory: < <= > >= == != • porovnávanie má zmysel len keď ukazovatele: • sú rovnakého typu • ukazujú na ten istý úsek pamäte • výsledok porovnania: • ak je podmienka splnená: 1 • inak: 0

  41. Porovnávanie ukazovateľov: príklad - výpis reťazca ... char *p1, *p2 , str[N]; strcpy(str, "ahoj"); p1 = str; p2=p1; while(p2 < p1+ N && *p2 != '\0') printf("%c", *p2++); str: pole s N znakmi, p1, p2: ukazovatele • vyisuje znaky pokiaľ: • nepresiahne dĺžku pridelenej pamäte premennej str a • pokým nedosiahnekoniec zapísaného slova

  42. int n, *p; ... if(n >= 0) p = alokuj(n); else p = NULL; ... if (p != NULL) ... Porovnávanie ukazovateľov s konštantou NULL • bez explicitného pretypovávania • p = NULL: • neukazuje na žiadne zmysluplné miesto v pamäti

  43. n = ((int *) p1 - (int *) p2) / sizeof(*p1); príklad: char *p1, *p2 , str[N]; ... for (p2=p1; p2<N && *p2 != '?'; p2++) ; printf("%d", (p2 < p1+N) ? (p2-p1+1) :-1); Rozdiel dvoch ukazovateľov rovnakého typu intn, *p1, *p2; ... n = p1 - p2; ak je v bloku pamäte '?', vypíše pozíciu, inak -1

  44. 1 p1: 10 2 3 7 p2: 50 Ukazovateľová aritmetika • aritmetické operácie: • Súčet ukazovateľa a celého čísla • Rozdiel ukazovateľa a celého čísla • Porovnávanie ukazovateľov rovnakého typu • Rozdiel dvoch ukazovateľov rovnakého typu • majú zmysel len vtedy, keď: • sú ukazovatele na rovnaký typ • ukazujú na ten istý úsek pamäte (OS nezaručí, že neskôr alokovaný blok bude na vyššej adrese)

  45. Ukazovateľová aritmetika • aritmetické operácie: • Súčet ukazovateľa a celého čísla • Rozdiel ukazovateľa a celého čísla • Porovnávanie ukazovateľov rovnakého typu • Rozdiel dvoch ukazovateľov rovnakého typu • majú zmysel len vtedy, keď: • sú ukazovatele na rovnaký typ • ukazujú na ten istý úsek pamäte (OS nezaručí, že neskôr alokovaný blok bude na vyššej adrese) 7 p2: 15 p1: 20 1 2 3

  46. Dynamické prideľovanie a uvoľňovanie pamäte • prideľovanie pamäte za chodu programu • v zásobníku (stack) - riadi operačný systém • v hromade (heap) - riadi programátor budeme sa zaoberať týmto prideľovaním • pomocou run-time funkcií • životnosť dynamických dát: • od alokovania po uvoľnenie pamäte

  47. Marienka, vy ste moje najlepšie pamäťové médium!

  48. void *malloc(unsigned int) Prideľovanie pamäte • pomocou funkcie definovanej v stdlib.h (niekedy v alloc.h): počet Bytov Adresa prvého prideleného prvku - je vhodné pretypovať.Ak nie je v pamäti dosť miesta, vráti NULL.

  49. Testovanie pridelenia pamäte • kontrola, či malloc() pridelil pamäť: int * p_i; if((p_i = (int *) malloc(1000)) == NULL) { printf("Nepodarilo sa pridelit pamat\n"); exit; }

  50. void free(void *) príklad: char *p_c; p_c = (char *) malloc(1000 * sizeof(char)); ... free(p_c); p_c = NULL; Uvoľňovanie pamäte • nepotrebnú pamäť je vhodné ihneď vrátiť operačnému systému • pomocou funkcie:

More Related