750 likes | 955 Views
Procedur álne programovanie: 6 . prednáška. Gabriela Kosková. Obsah. príklady (defin ície + 1 príklad) jednorozmern é polia príklady (3) re ťazce. Procedur álne programovanie: Pr ác a s ukazovateľmi - príklad y. Pr íklady definícií. typu int. - x je. int x;. typ int.
E N D
Procedurálne programovanie:6. prednáška Gabriela Kosková
Obsah • príklady (definície + 1 príklad) • jednorozmerné polia • príklady (3) • reťazce
Príklady definícií typu int -x je int x; typ int ukazovateľ na -y je int *y; ukazovateľ na int int *z(); funkcia vracajúca -z je funkciu vracajúcu ukazovateľ na int -v je int (*v)(); ukazovateľ na funkciu vracajúcu -w je int *(*w)(); ukazovateľ na int
Príklad program načíta celé číslo n a alokuje blok pamäte pre n celých čísel. Od používateľa čísla načíta. Nakoniec vypočíta ich priemer.
#include <stdio.h> #include <stdlib.h> int *alokuj(int pocet); void nacitaj(int *pole, int pocet); float priemer(int *pole, int pocet); void vypis(int *pole, int pocet); int main() { int *pole, n; printf("Zadajte pocet cisel: "); scanf("%d", &n); if ((pole = alokuj(n)) == NULL) { printf("Nepodarilo sa alokovat pole.\n"); return 1; } nacitaj(pole, n); printf("Priemer cisel: \n"); vypis(pole, n); printf("je %.3f.\n", priemer(pole, n)); free(pole); return 0; }
int *alokuj(int pocet) { return (int *) malloc(pocet * sizeof(int)); } void nacitaj(int *pole, int pocet) { int i; for(i = 0; i < pocet; i++) { printf("%d-te cislo: ", i); scanf("%d", pole + i); } } float priemer(int *pole, int pocet) { int i, suma = 0; for(i = 0; i < pocet; i++) suma += *(pole + i); return (float) suma / (float) pocet; }
void vypis(int *pole, int pocet) { int i; for(i = 0; i < pocet; i++) printf("%d, ", *(pole + i)); }
N: 3 TYP: int index: 5 0 6 1 7 2 Základy práce s poliami • pole je štruktúra zložená z niekoľkých prvkov rovnakého typu (blok prvkov) statická definícia poľa TYP x[N]; • pole obsahuje N prvkov • dolná hranica je vždy 0 • horná hranica je N-1 • číslo N musí byť známe v čase prekladu • hodnoty nie sú inicializované na 0
Príklady definícií statického poľa #define N 10 int x[N], y[N+1], z[N*2]; x má prvkov poľa, od indexu po index y má prvkov poľa, od indexu po index z má prvkov poľa, od indexu po index 10 0 9 11 0 10 0 20 19
priradenie hodnoty do prvého prvku poľa v cykle priradenie hodnoty postupne všetkým prvkom poľa x[0] = 1; for (i = 0; i < N; i++) x[i] = i+1; výpis prvkov poľa for (i = 0; i < N; i++) pritnf("x[%d]: %d\n", i, x[i]); Prístup k prvkom poľa #define N 10 ... int x[N], i;
pokračovanie Príklad statického poľa: histogram písmen v reťazci #include <stdio.h> #include <stdlib.h> #define N ('Z' - 'A' + 1) int main() { int i; char hist[N], slovo[100]; scanf("%s", slovo); /* nacitanie slova */ for (i = 0; i < N; i++) /* inicializacia hist */ hist[i] = 0; i = 0; /* naplnenie hist */ while (i < 100 && slovo[i] != '\0') { hist[toupper(slovo[i]) - 'A']++; i++; }
Príklad statického poľa: histogram písmen v reťazci pokračovanie: for(i = 0; i < N; i++) /* vypis hist */ if(hist[i] != 0) printf("%c: %d\n", i+'A', hist[i]); return 0; }
Inicializácia poľa v definícii ... int x[3] = { 1, 2, 3 }; int y[] = { 1, 2, 3 }; int z[5] = {1, 2, 3 }; ... int n = 5; int z[n]; Počet prvkov poľa je daný počtom hodnôt Hodnoty z[3] a z[4] sú inicializované na 0. Nie je povolené, keďže n je premenná a nie číslo alebo konštanta (t.j. hodnota nemusí byť známa v čase prekladu)
Polia a ukazovatele • adresa i-teho prvku poľa x: &x[i] = bázová adresa x + i * sizeof(typ) • x je adresa v pamäti • platí: x + i == &x[i] *(x + i) == x[i]
Polia a ukazovatele • platí: • p[0] == *p • p[1] == *(p + 1) • p[2] == *(p + 2) • p[3] == *(p + 3) int *p; p = (int *) malloc(4 * sizeof(int)); Rozdiel medzi statickými a dynamickými poliami je najmä v spôsobe prideľovania pamäte.
Polia a ukazovatele • platí: • &x[0] == &*(x + 0) == x • &x[i] == &*(x + i) == (x + i) int x[4]; x je statický ukazovateľ, nemôžeme spraviťx = p_i; môžeme ale urobiť *x = 2; (to isté ako x[0] = 2;)
i = 0; /* naplnenie pola */ while (i < N && (*(slovo +i) != '\0') { hist[toupper(*(slovo +i)) - 'A']++; i++; } Príkladprístupu k prvkom poľa pomocou ukazovateľa Prepísanie nasledujúcej časti programu tak, aby sa k poľu slovo pristupovalo prostredníctvom ukazovateľov. i = 0; /* naplnenie pola */ while (i < N && (slovo[i] != '\0'){ hist[toupper(slovo[i]) - 'A']++; i++; }
Zistenie veľkosti poľa • po alokovaní pamäte pre p_x budú x aj p_x ukazovatele na pole 10 prvkov typu int, s rozdielom, že: • x je statický ukazovateľ • p_x je dynamický ukazovateľ • preto dáva sizeof() iné výsledky: sizeof(x) == 10 * sizeof(int) (napr. 20) sizeof(p_x) == sizeof(int *) (napr. 4) int x[10], *p_x; p_x = (int *) malloc (10 * sizeof(int));
Pole meniace svoju veľkosť alokovanie poľa x int *x, n = 5, *p1, *p2, *p; x = (int *) malloc(n * sizeof(int)); x[0] = 10; x[4] = 3; ... /* potreba zvacsit pole*/ p = (int *) malloc (10 * n * sizof(int)); p1 = x; p2 = p; while(p1 < x + n) *p2++ = *p1++; n *= 10; free(x); x = p; alokovanie poľa p kopírovanie obsahu poľa uvoľnenie menšieho poľa x nastavenie x na p
Pole meniace svoju veľkosť - pomocou realloc() • funkcia void *realloc(void *pole, unsigned int size) definovaná v stdlib.h • pole - ukazovateľ na pamäť • size - veľkosť • zväčší pole, alebo vytvorí nové a prekopíruje tam hodnoty z pôvodného poľa x = realloc(x, 10 * n * sizeof(int));
vráti maximum z prvkov poľa pole Pole ako parameter funkcie • identifikátor nasledovaný zátvorkami: int pole[] int maximum(int pole[], int n) { int *p_max = pole, *p; for (p = pole + 1; p < pole + n; p++) { if (*p > *p_max) p_max = p; } return (*p_max); } nepozná veľkosť poľa, preto ju treba uviesť
parameter sa dá vo funkcii meniť (lebo sa vytvorila jeho lokálna kópia (nezáleží na tom, či ide o statické alebo dynamické pole) Pole ako parameter funkcie: veľkosť poľa • vo funkcii sa nedá zistiť veľkosť poľa aj keď: int maximum(int pole[], int n) int maximum(int pole[10]) • parameter bude stále považovaný za pole[]
Volanie funkcie s poľom ako parametrom: max = maximum(pole, 10); Pole ako parameter funkcie int *pole je ekvivalentné int pole[] Pri použití int pole[] je jasnejšie, že ide o pole a nie o ukazovateľ na int.
int maximum(int *pole, int n) {...} int x[10]; max = maximum(x + 2, 5); int x[10]; max = maximum(&x[2], 5); Pole ako parameter funkcie • dá sa použiť aj na zistenie maxima napr. na zistenie maxima 3. až 7. prvku int maximum(int pole[], int n) {...}
Pole ako parameter funkcie • prepísanie funkcie maximum()na procedúru void maximum(int pole[], int n, int *p_max) { int *p; *p_max = pole[0]; for (p = pole + 1; p < pole + n; p++) { if (*p > *p_max) *p_max = *p; } } ak by sme dali p_max = p; stratili by sme ukazovateľ na premennú, kam treba vrátiť maximum
void main() { double *p_d; init(&p_d); } Pole ako parameter funkcie: vytváranie poľa vo funkcii void init(double **p_f) { double a[5]; int i; for(i = 0; i < 5; i++) { printf("Zadaj %d. cislo"); scanf("%lf", &a[i]); } *p_f = a; } double *a; int i; a = (double *) malloc(5 * sizeof(double); p_d bude ukazovať na pole 5 double prvkov, ale a bolo vyrobené v zásobníku, a tento zásobník sa pri ukončovaní funkcie zruší
Pole ukazovateľov na funkcie • prvkami poľa môžu byť aj ukazovatele • na prvky viacrozmerné polia • na funkcie (všetky funkcie musia byť toho istého typu) definícia ukazovateľa na funkciu vracajúcu typ void typedef void (* P_FNC)(); P_FNC funkcie[10]; pole 10 ukazovateľov
funkcia[1](); Pole ukazovateľov na funkcie • pole ukazovateľov na funkcie pri riadení programu pomocou menu typedef void (* P_FNC)(); P_FNC funkcie[5] = {file, edit, search, compile, run}; ... • volanie funkcie:
Ako čítať zložitejšie definície 2 • do čítania definícií treba začleniť polia • definície sa čítajú rovnako, len treba brať do úvahyaj []
Príkady zložitejších definícií -f je pole ukazovateľov na funkcie vracajúce typ double double(*f[])(); -f je funkcia vracajúca ukazovateľ na pole prvkov typu double double(*f())[]; -f je polefunkcií vracajúce ukazovateľ na typ double double*(f[])(); -f je funkcia vracajúca pole ukazovateľov na typu double double*f()[];
pole prvkov typu Ako čítať zložitejšie definície 2 • Nájdeme identifikátor - f je funkcia vracajúca double (* f())[]; (* f())[]; (* f()) (* f())[] f() f()) f double (* f())[]; ukazovateľ na double a hľadáme ) doprava • Nájdeme () a pokračujeme doprava • Nájdeme ) a k nej zodpovedajúcu ( a pokračujeme doprava • Nájdeme [] , pokračujeme doprava po ; , doľava • Nájdeme double
Príklad 1 Program vytvorí histogram výskytov písmen v súbore (pre každé písmeno - počet jeho výskytov)
pokračovanie #include<stdio.h> #include<stdlib.h> #define SUBOR "pismena.txt" #define N 'Z' - 'A' + 1 int main() { int c, i, hist[N]; FILE *fr; if((fr = fopen(SUBOR, "r")) == NULL) { printf("Subor sa nepodarilo otvorit.\n"); return 1; } for (i=0; i<N; i++) hist[i] = 0; while ((c = toupper(getc(fr))) != EOF) { if(c >= 'A' && c <= 'Z') hist[c - 'A']++; }
Príklad1 pokračovanie: for (i=0; i<N; i++) if (hist[i] != 0) printf("%c: %2d\n", 'A' + i, hist[i]); if(fclose(fr) == EOF) { printf("Subor sa nepodarilo zatvorit.\n"); return 1; } return 0; }
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Príklad 2: Eratostenovo sito • algoritmus na nájdenie prvočísel v poli • vyškrtáva všetky násobky prvočísel, počnúc 2 (vyškrtnutie prepísanie čísla na 0) 0 0 0 0 0 0 0 0 0 i: prv[i]: 2 1 0 2 3 0 v cykle, i od 0 do 2 poľa prv: v cykle, k od i+1 do 14: vyškrtneme všetky násobky čísla prv[i]
pokračovanie Príklad 2: Eratostenovo sito #include <stdio.h> #include <stdlib.h> #include <math.h> void main() { int n, i, k, odm; int *prv; printf("Do ktoreho cisla hladat prvocisla? "); scanf("%d", &n); n--; /* znizime n o 1, nevyskrtavame nasobky jednotky */ prv = (int *) malloc(n * sizeof(int)); for(i = 0; i < n; i++) /* inicializacia */ prv[i] = i+2;
pokračovanie: odm = (int) sqrt(n)-1; printf("odmocnina: %d\n", o); for(i = 0; i < odm; i++) { if(prv[i] != 0) { /* ak sme predtym cislo nevyskrtli */ for(k = i+1; k < n; k++) { /* vyskrtni vsetky nasobky */ if(prv[k] != 0) { /* ak este nie je vyskrtnute */ if(prv[k] % prv[i] == 0) /* ak je delitelne */ prv[k] = 0; } } } } printf("Prvocisla: "); /* vypisanie prvocisel */ for(i = 0; i < n; i++) if(prv[i] != 0) printf("%d, ", prv[i]); putchar('\n'); return 0; }
Príklad 3 program na rýchle kopírovanie bloku pamäte z miesta, kam ukazuje p1 na miesto, kam ukazuje p2. Treba použiť pomocný ukazovasteľ p ... for (p = p1; p < p1 + N; *p2++ = *p++) ;
Čo sú to reťazce • reťazce sú jednorozmerné polia typu char • dĺžka reťazca: ľubovoľná, obmedzená veľkosťou pamäte • z celkovej pamäte je aktívna len časť od začiatku poľa do znaku '\0' ukončovací znak • ak nie je reťazec ukončený znakom '\0', považuje sa za reťazec celá pamäť až do najbližšieho znaku '\0'
char s[] = "abrakadabra"; char *s; s = (char *) malloc(6); Definícia a inicializácia reťazca • reťazec s najviac 6 znakmi: • staticky: • dynamicky: char s[6]; = "ahoj"; a s: 0 h 1 o 2 inicializuje sa miesta práve pre daný text j 3 \0 4 5
Definícia a inicializácia reťazca pridá na koniec '\0' char s[15] = "abrakadabra"; v C nie je možné takto priradiť statickému reťazcu konštantu char s[10]; s = "ahoj"; tu s nepredstavuje dynamický reťazec, ale ukazovateľ na typ char a je inicializovaný adresou konštanty (pre ktorú je vyhradené miesto) char *s= "ahoj";
Inicializácia dynamického reťazca? • dynamicky vytvorený reťazec sa nedá inicializovať! alokovanie 10 znakov do s char *s; s = (char *) malloc(10); s = "ahoj"; char c; int i=0; while ((c=getchar()) != '\n' && i < 9) s[i++] = c; s[i] = '\0'; načítanie aj pomocou scanf
Poznámky k definícii a inicializácii reťazcov • definícia typu pre reťazce • treba rozlišovať: • nulový ukazovateľ NULL a nulový reťazec '\0': • nulový ukazovateľ neukazuje na žiadne miesto v pamäti, • nulový reťazec má v 0-tom prvku znak '\0' • "x" a 'x': • "x" je reťazec s jedným znakom ukončený '\0' (2 Byty) • 'x' je jeden znak (1 Byte) typedef char *STRING;
Čítanie reťazca z klávesnice sem nepartrí &, pretože s je adresa • scanf() vynecháva biele znaky a číta po prvý biely znak • ak je na vstupe " ahoj Eva!", scanf() prečíta iba "ahoj" a zvyšok zostáva v bufferi klávesnice char s[10]; ... scanf("%s", s);
Formátované čítanie: príklad Program vypočíta celkovú sumu peňazí zo súboru, kde jednotlivé sumy sú vždy uvedené znakom $ a znamienko + je pre príjem a - pre výdaj Príklad súboru: + $10 -$5- $8 +$20
Formátované čítanie: príklad include <stdio.h> void main() { FILE *f; int kolko, suma = 0; char akcia[2]; f = fopen("peniaze.txt", "r"); while (fscanf(f, "%1s", akcia) != EOF) { fscanf(f, "$%d",&kolko); suma += (akcia[0] == '+') ? kolko : (-1*kolko); } printf("Spolu: %d\n", suma); fclose(f); } namiesto %1s nemôže byť %c, pretože by prečítal medzeru, nie prvý znak podobne $%d: $ zabezpečuje, že sa preskočia biele znaky