11a Dynamické dvourozměrné pole (obdobně vícerozměrné) počet hvězdiček == počet rozměrů (dimenze pole) int **p_2d; int radku, sloupcu; printf("zadejte pocet radku a sloupcu pole:"); scanf("%d,%d", &radku, &sloupcu) p_2d = (int **) malloc(radku * sizeof(int *)); for (int i = 0; i < radku; i++) *(p_2d + i) = (int *) malloc(sloupcu * sizeof(int)); nebo for (int i = 0; i < radku; i++) p_2d[i] = (int *) malloc(sloupcu * sizeof(int)); int **p_2d int int * Zeleně označené hodnoty rozměry pole by v praxi byly proměnné.
Použití opět jako "normální" pole (viz výše): p_2d[radek][sloupec] = 5; p_2d[2][3] = 5; identická operace "pointerovým" způsobem: *(*(p_2d + radek) + sloupec) = 5; *(*(p_2d + 2) + 3) = 5;
Uvolnění paměti 2D dynamického pole int i; for (i = 0; i < radku; i++) free((void *) *(p_2d + i)); free((void *) p_2d); resp. bez přetypování (umí samo) int i; for (i = 0; i < radku; i++) free(*(p_2d + i)); free(p_2d); Jinak pomocí [ ] int i; for (i = 0; i < radku; i++) free(p_2d[i]); free(p_2d); Obecně u dynamických polí platí: &pole[0] == &*(pole + 0) == pole, tj. adresa nultého prvku pole je rovna adrese hodnoty nultého prvku pole a lze ji vyjádřit samotným názvem pole (bez hranatých závorek) Tento přístup k polím (pomocí pointerů) umožňuje i následující postup: double **p_p_double, *p_prvek, *p_radek;
p_p_double = (double **) malloc(4 * sizeof(double *)); for (int i = 0; i < 4; i++) p_p_double[i] = (double *) malloc(3 * sizeof(double)); p_radek = p_p_double[2]; Dále pracuji s p_radek, jako s jednorozměrným polem, nijak není vidět, že vlastně upravuji řádek 2d pole p_double s indexem 2 p_radek[1] = 6.54; vykoná totéž jako: p_p_double[2][1] = 6.54; Obdobně pro jeden prvek: p_prvek = &(p_p_double[2][1]); A opět obdobná operace: *p_prvek = 6.54;
Př. funkce, které posíláme na zpracování jen jeden řádek void vypln_radek(double *p_radek, double cim) int i; for(i = 0; i < POCET_SLOUPCU; i++) p_radek[i] = cim; Potom v např. mainu: int main(void) double **p_double, *p_prvek, *p_radek; p_double = (double **) malloc(4 * sizeof(double *)); for (int i = 0; i < 4; i++) *(p_double + i) = (double *) malloc(3 * sizeof(double)); p_radek = p_double[2]; vypln_radek(p_radek, 3.45); vypln_radek(p_double[2], 3.45); // totéž bez pomocné prom.p_radek // uvolnění paměti 2D dynamického pole for (int i = 0; i < 4; i++) free((void *) *(p_double + i)); free((void *) p_double);
Pointery ve vztahu k funkcím mějme funkci deklarovanou takto: void pocitej(double *p_param) mohu volat v programu různými způsoby. Záleží na tom, co je obsahem funkce, tj. musím vědět, jaký parametr chce. Uvedený parametr může být jeden double, nebo 1D pole double (což ovšem může být i jeden řádek 2D pole) Tj.: double a; double *p_d; double **p_pole2d; /* zde proběhne alokace paměti, neuvádím, viz výše */ pocitej(&a); /* předám adr. jednoho double */ pocitej(p_d); /* předám adr. dyn. alokované paměti jeden nebo více double (tj. třeba jednorozměrné pole) */ pocitej(p_pole2d[2]); /* předám adr. dyn. alokované paměti jeden nebo více double jeden řádek 2D,tj.totéž jako jednorozměrné pole) */ To, která varianta je ta pravá, rozhoduje vnitřek funkce pocitej.
Např. pro 1D pole (nebo jeden řádek 2D pole) Je lépe předat také rozměr pole: void pocitej(double *param, int delkapole) double pom; pom = 6.98; for (int i = 0; i < delkapole; i++) param[i] = param[i] * pom; Potom volání: double *p_d; p_d = (double *) malloc(10 * sizeof(double)); pocitej(p_d, 10); Lze i pro staticky vytvořené pole: double policko[10]; pocitej(policko, 10); Pro 2D pole: void pocitej2d(double **param, int poc_radek, int poc_sloupcu) int i, j; double pom; pom = 6.98; for (i = 0; i < poc_radek; i++) for (j = 0; j < poc_sloupcu; j++) param[i][j] = param[i][j] * pom; Obdobným způsobem může funkce vracet výsledek typu pointer int * vyrob_1dpole(int delka_pole)
int *p_prom; p_prom = (int *) malloc(delka_pole * sizeof(int)); if (p_prom == NULL) printf("mas smulu, pamet dosla ;-)"); exit(0); // např. ukončení programu else return(p_prom); Volání této funkce: int *p_pole; p_pole = vyrob_1dpole(30); Pozor na závážnou chybu: int * vyrob_1dpole(int delka_pole) int prom[delka_pole]; /* půjde jen v některých verzích C (ANSI99) */ return(prom); Horší případ (projde přes všechny překladače) a opět velká chyba, vracím odkaz na paměť která na konci funkce je uvolněna!!!!: int * vyrob_1dpole100(void) int prom[100]; return(prom); // vracím adresu kde to této chvíle BYLO pole, ale po návratu z funkce už nebude, ale já to nemám šanci poznat => budu pracovat s oblastí paměti, kde nemám co dělat!!!! Zdánlivě bude fungovat, vrátí adresu pole prom. Bude chybovat, protože prom je lokální proměnná, po skončení funkce zanikne tj. vracím sice
adresu paměti, ta však byla po skončení funkce vrácena systému jako volná => budu přepisovat, používat oblast paměti, která mi již nepatří. Mohou být v ní vytvořeny další proměnné a já si je nechtíc přepíši. Pozn.: různé "hezké" deklarace komplikovaných pointerů viz učebnice double (* nazev())[]; - nazev je funkce vracející pointer na pole doublů double (* nazev[])(); - nazev je pole pointerů na funkce, které vracejí double Pozn.: bylo zmíněno pole pointerů na funkce. Jak zjistit adresu funkce: nazev_funkce bez kulatých závorek, jen název adresa Např.: mám funkci: double pocitej(void)...tělo funkce... void main(void) double x; double (* p_funkce)(); p_funkce = pocitej; // do p_funkce jsem dal adresu funkce pocitej x = p_funkce(); - vyvolá totéž jako příkaz x = pocitej();
Např.: #include <stdio.h> #include <math.h> #define DELKA 3 main() double cislo = 2.45; int i; double (* p_fun[delka])(double); printf("\nsin %f je %f", cislo, sin(cislo)); printf("\ncos %f je %f", cislo, cos(cislo)); printf("\ntan %f je %f\n", cislo, tan(cislo)); p_fun[0] = sin; p_fun[1] = cos; p_fun[2] = tan; for(i = 0; i < DELKA; i++) printf("\nvysledek pro %f je %f", cislo, p_fun[i](cislo)); printf("\n"); system("pause");