Pokud alokujes int, tak se alokuje na plnou delku sveho datoveho typu (an AVR tusim 2B), bez ohledu na hodnotu, jakou do nej das. Tedy int bude zabirat stejne misto v pameti, at uz v nem budes mit celou dobu pouze 0 (a nikdy ji nezmenis), nebo do nej budes davat hodnoty v celem jeho rozsahu.
Dalsi vec je, ze prekladac muze umistovat delsi datove typy na "zarovnane adresy", ale to uz je dost pokrocila zalezitost.
lokalni promenna - plati jen v nejakem bloku (treba funkci), alokuje se az vypocet do toho bloku dojde a az ho opusti, tak se dealokuje. Proto taky nikdy nevracej z funkce ukazatele na jeji lokalni promenne (protoze mimo tu funkci uz "neexistuji" a co tam v te pameti vlastne je neni nijak zaruceno (kdokoli to mohl prepsat cimkoli a mozna to i aktivne pouziva) - alokuji se na zasobniku. V Cecku jsou i mensi bloky nez funkce a na jejich lokalni promenne se to vztahuje taky.
Dynamicka alokace - zjednodusene malloc nebo new - alokuje misto pro data za behu programu (a zase se to misto da uvolnit - free nebo delete).
- pokud udelas cyklus "for(;;){malloc(2);};", tak ti postupne naalokuje veskerou volnou pamet.
Pouziva se to zejmena kdyz
- potrebujes nejakou pamet, ale kolik ji bude zjistis az za behu programu
- potrebujes nejakou pamet na nejakou chvili (delsi nez volani te dane funkce) a pak ji zase chces uvolnit az ji nebudes potrebovat (typicky ti funkce nekde neco nacte a ma ti to nejak vratit - kdyby to mela v lokalni promenne, tak se skoncenim funkce
Alokace na zasobniku - to jsou "normalni lokalni promenne ve funkci" - alokuji se na zasobniku kdyz se funkce spusti a dealokuji se kdyz funkce skonci (a tim se vrati zasobnik) - pri dalsim volani funkce se alokuji znovu, takze si "nepamatuji" jakou mely hodnotu pri minulem volani funkce
-
Kód: Vybrat vše
void pokus(){
int x;
return;
}
...
for(;;){ pokus(); };
ti pri kazdem zavolani funkce "pokus" ulozi na zasobnik adresu, kam se ma po skonceni volani funkce vratit, pak na zasobniku alokuje misto pro promennou "x" (tim posune zasobnik o dalsi 2B); pak kdyz funkce skonci, tak uvolni "x", vyzvedne ze zasobniku navratovou adresu, skoci na ni a pokracuje dalsim prikazem. Tady je v nekonecnem cyklu, takze ten zasobnik porad dokola naroste o 4B (adresa a "x") a zase se o ne zmensi - pamet nikdy nedojde.
Rekurze - kdyz funkce vola sebe samu (primo, nebo prez prostrednika, vetsinou podle nejake podminky a je to obcas velmi elegantni reseni a obcas velky problem, kdyz se to udela spatne)
Kód: Vybrat vše
void pokus(){
int x;
pokus();
return;
}
...
pokus();
tady je neomezena (coz je spatne), ale zase pri kazdem zavolani funkce "pokus" ulozi na zasobnik adresu, kam se ma po skonceni volani funkce vratit, pak na zasobniku alokuje misto pro promennou "x" (tim posune zasobnik o dalsi 2B) pak ZNOVA zavola funkci pokus, tedy znova da na zasobnik navratovou adresu a znova si tam vyhradi misto po "x" (tedy dalsi 2+2B), dokud nevycerpa veskerou pamet.
globalni alokace - to jsou promenne deklarovane mimo jakoukoli funkci - prekladac jim priradi misto v pameti uz pri prekladu a muze k nim pristupovat kazda funkce - zabiraji RAM celou dobu (jeste driv, nez se program spusti)
staticka alokace - i uvnitr funkce muzes deklarovat "globalni promennou", ktera se alokuje pred zacatkem programu a co do ni funkce pri jednom volani zapise, to tam pri jinem volani najde. Je tedy alokovana i kdyz funkce sama (zrovna) zavolana neni. Od globalnich promennych se lisi jen tim, ze je ostatni funkce "nevidi" a nemuzou je tudiz pouzivat. Casto se takto deklaruji ruzne buffery, nebo jine podobne veci a funkce muze jine funkci predat jejich adresu, takze je jine funkce muzou pouzit prez ten ukazatel. poznaji se podle klicoveho slova "static"
Kód: Vybrat vše
void pokus(){
static int x;
return;
};
ukazatel - je promenna obsahujici adresu cehosi (casto dat) (obdobne jako char je promenna obsahujici znak a int je promenna obsahujici cislo) - na AVR vetsinou zabira 2B.
takze
deklaruje promennou jmenem "a", ktera zabira 2B a je typu ukazatel (na hodnotu typu int) a ten malloc(2) dynamicky alokuje DALSI 2B kdesi v pameti a do te promenne "a" ulozi jejich adresu. (ukazatel si muzes predstavit i jako "index bytu v pameti" kdy na celou pamet pohlizis jako na pole)
Ukazatele se pouzivaji pro:
- pristup k dynamicky alokovane pameti
- vytvareni seznamu libovolne delky (omezeno pameti, ale muzes mit ukazatel na strukturu, co ma v sobe ukazatel na dalsi strukturu a takhle vytvoris seznam, kdy od jedne jdes k druhe, treti ... az dokud ten ukazatel neni NULL)
- pro predavani adresy dynamicke pameti alokovane ve funkci mimo funkci ven
- pro predavani jakekoli adresy kamkoli je treba (muzes mit klidne nekolik globalnich promennych stejneho typu a udelas si ukazatel na tento typ a pak ho predas funkci, ktera neco udela s mistem, kam ten ukazatel ukazuje - napriklad ho vypise na Serial)
- pro predavani adresy pole do funkce (ktera pak do toho pole muze treba zapisovat data)
- pro predavani adresy funkce (ano i to jde) - a pak muzes mit funkci, ktera aplikuje "neco" na spoustu dat a co presne ma aplikovat ji predas jako ukazatel na funkci, co to aplikuje (hledani maxima, minima, scitani, prumer, preneseni prez nejake obskurni medium ...)
- spousty jinych zajimavych veci (a ano, da se tim velice zajimavym zpusobem strelit do nohy)
Rozdil mezi alokaci v setupu a loop a jine funkci neni zadny, jenom ze setup se dela jen jednou a loop vetsinou opakovane a funkce podle toho kdy ji zavolas. Ja tim chtel rict jen to, ze si tu pamet alokuju jen jednou (v setupu), a pak uz ji porad dokola pouzivam (v loop a funkcich volanych z loop) a neuvolnuju ji, protoze ji pouzivam porad dokud to arduino bezi (a tudiz mi bezi opakovane loop) a az se vypne, tak uz ji zase neni potreba uvolnovat, protoze pri novem startu zadna pamet alokovana neni (dokud to nedobehne do setupu a neudelam novou alokaci)