Je to totalne cele spatne a funguje to ciste souhrou okolnosti.
zacnu nejdriv od konce - zadny problem jsi sice nezaznamenal, ale neznamena, ze by tam zadny nebyl a ze by ti to klidne mohlo i spadnout, nebo delat dost nepredpokladane veci. Ale ten programek je tak maly a jednoduchy a nedela skoro nic, takze ti to nejspis nahodou z tohoto duvodu proslo.
Jako kdyz vlezes do prazdneho mistenkoveho vlaku s jednou mistenkou, sednes si na to misto, na druhe si das tasku, na treti noviny a na ctvrte hamburgr. Pak to zase rychle sbalis a nez prijde pruvodci, nebo jini cestujici, tak vypadnes z vlaku a jdes domu s pocitem, ze vse funguje a zadny problem jsi nezaznamenal.
Pricemz o spouste veci (zatim) nevis (ze sedadlo naproti ma od dalsi stanice rezervovane nekdo jiny) a mnohe treba ani vedet nepotrebujes (jak funguje motor lokomotivy, napriklad).
Pro zacatek odhledneme od toho, ze kompilator ma pomerne velkou volnost, jak tvuj program optimalizovat uz pri prekladu a ze vysledny kod vlastne vubec nemusi vypadat jako to, co jsi napsal, jen by se tak mel v dusledku pro vnejsiho pozorovatele jevit (pokud jsi to vsechno napsal spravne - pokud ne, tak ma v mnoha pripadech kompilator moznost udelat absolutne cokoli, i kdyz vetsinou jen vypise nejake varovani, nebo si usetri praci a tvoje chyby neresi a jak to vyjde, tak to vyjde).
A operacni system se stara nejen o tvuj program ale i o spoustu dalsich co bezi "naraz" s nim (at uz to znamena co chce) a o pamete (kdyz potrebuje RAM, tak treba tvoje promenne ulozi na disk do swapu, program rovnou prepise (protoze si ho muze kdykoli zase z disku nacist), RAM pouzije na cokoli jineho a pak zase vrati veci zpatky a tvuj program o tom nema ani tuseni.
A protoze toho ten system ma na praci hodne, tak se vetsinou nestara o jeden kazdy byte, ktery si na nem vyzadas, ale vyhradi pri prvnim pozadavku te pameti nejaky uceleny blok, pri dalsim pozadavku na pamet proste vrati ukazatel na dalsi misto v bloku, co ti jeste nepridelil a teprve az ten blok vycerpa a ty chces vic, tak ti prideli dalsi blok a zase z nej bere ...
Takze jako v tom rychliku, kdyz se o kousek seknes, muze se stat, ze tam bude zrovna volno a projde ti to, protoze ve vlaku je cely vagon. A nebo tam zkusis dat tech veci vic a najednou jsi na konci vagonu a dalsim krokem spadnes na koleje. Nebo jen das neco do vagonu, co se bude po ceste preprahat a pojede jinam a najednou pulku veci nemas. Ale protoze sis nekoupil mistenky (nealokoval patricne pamet), tak je to tvuj problem. Nebo te chytne pruvodci a z vlaku vyhodi.
Ale nez se tvuj program spusti, tak se stane spousta veci, napriklad se jeho kod ulozi nekam do pameti, nekde v pameti se alokokuje misto pro vsechny texty, ktere tam mas (treba %d z tech printu), misto pro zasobnik, misto pro staticke promenne, misto pro haldu a spusta dalsiho.
Pak se tvuj program spusti a magie pokracuje - knihovni funkce ti napriklad zaridi pristup ke standardnimu vstupu a vystupu (takze ten print ma kam psat), nastavi se promenne obsahujici zacatek a konec pouzite haldy (a taky treba seznam der v ni) a tak dal a tak dal.
Pak se po ruznych peripetiich spusti tvoje funkce main a (krome jinych veci) ti na zasobniku vytvori misto pro promennou heapArray, co by mela obsahovat ukazatel na int (teda az tam nejaky vrazis, pokud vubec).
Nasledne pozadas o ukazatel na jedno misto pro int (malloc) a dostanes mistenku (jeho navratova hodnota), ktera ti dava pravo si sednout a rika kam. (BTW v textu pises neco jako 3*sizeof(int), v kodu uz jen sizeof(int), takze nasledne indexy 1 a 2 uz jsou taky mimo hranice alokovane pameti)
Nasledne si na to misto sednes (heapArray[0] = 0;) a zatim je vse v poradku (akorat teda ten vlak ma z praktickych duvodu cely vagon, o kterem se vi, ze je v nem jedno misto obsazeno a zbytek volny, jde tedy na nej prodat mistenky = alokovat ho pro cokoli si rekne)
Pak na dalsi misto das tasku (heapArray[1] = 1) - ale ackoli tam nahodou zrovna misto je (protoze system ti tam dal z praktickych duvodu celou stranku) tak uz ti nepatri (v malloc sis zarezervoval jen 1 int) ackoli sis doma rikal, ze ji ty mistenky koupis tri (v tom uvodnim textu).
Takze pokud by kdokoli v tu chvili pozadal malloc o nejake misto, tak dostane prideleno ( = mistenku na, ) to misto hned za tebou, kde ted lezi taska. Pri trose smuly by to mohl byt i ta funkce print o par radku niz, co vypisuje heapArray[0] a mohla by si tam popravu dat cokoli by ji napadlo ( tim zrusit tvoji tasku, teda tu hodnotu 1 prepsat cimkoli) a pokud by pak to misto vratila, tak uz by tam tvoje taska proste nelezela. A pak by treba ten dalsi print vypsal misto tasky (heapArray[1] = 1) batoh s lyzema (nejake naprosto jine cislo), co tam byl naposled.
A ano, samozrejme, pokud tvoje sedadlo ma cislo 0, tak dalsi sedadlo ma cislo 1 a dalsi cislo 2 atd ... Takze si klidne muzes pocitat sedadla jak chces, i kdyz na ne mistenky nemas, jen ti nikdo nezaruci, ze bez tech mistenek ta sedadla budou volna a co tam das tam i zustane, nebo ze kdyz tam neco das, tak tim neznicis neco jinemu radnemu pasazerovi a nebude to mit necekane dusledky). Nebo ze kdyz tam neco zkusis dat a uz je to v dalsim vagonu a ten neni pripojeny, tak ze nespadnes na koleje (system nevyhodi chybu pristupu a nasilne neukonci tvuj program).
Takze zkus si udelat tohle:
- alokujes si jedno pole a vypises si jeho adresy . printf("%p ", &heapArray[0]); atd ...
- alokujes druhe pole a opet si vypises jeho adresy printf("%p ", &heapArray2[0]);
- do prvniho pole zapises dost veci, aby ti to vyslo az prez adresy toho druheho pole (treba cisla od 0 do 2*velikost_pole)
- do druheho pole zapises dost jinych veci (treba cisla od 1000 do 1000+velikost_pole)
- ted si zkus vypsat to prvni pole az do 2*velikost_pole a najednou uvidis, ze to od nejakeho cisla je uplne blbe
- tak to "oprav" zapis tam ta puvodni cisla jeste jednou
- ted zkus vypsat to druhe pole a "hrozne se div" proc ti nezacina 1000, ale necim uplne jinym (radove tak velikost_pole mozna plus nejake drobne na rezii)
Cimz mame odpoved na otazku, proc pri alokaci musis udavat pocet policek - protoze ti je nasledne alokovano jen tolik, kolik jsi zadal a nasledujici mohou byt alokovany pro nekoho jineho (treba heapArray2)
A proc to vubec "fungovalo" - protoze dopravce misto jedne sedacky pripojil cely vagon.
A proc je to uplne spatne - protoze kdyz pises nekam, kde sis to nealokoval, tak tam muze byt klidne neco jineho, co zmrsis (s naproste nahodnymi nasledky) a taky proto, ze kdyz pak odtamtud ctes, tak tam vubec nemusi byt to, co jsi tam zapsal (ale to, co tam zapsal ten druhy, co si alokoval kus pameti pro sebe = koupil mistenky - a pak si tam zapsal co chtel on)
A proc je to tak uplne spatne - protoze jakmile si takhle zacnes prepisovat pamet s nejakou jinou casti kodu (at tvoji, ci knihovni) a pak chces ty hodnoty pouzit, tak to vubec nemusi fungovat a vubec nemusis tusit proc - a nekdy ano a nekdy ne.
A proc je to jeste hur - proteze vubec netusis, co si kde ktera knihovni funkce uklada do pameti, kterou si pro to alokovala a co se stane, kdyz se ji najednou pod rukama ty hodnoty zmeni, protoze ty si pises kam chces co chces, u kdyz nemas mistenky.
(cimz se klidne mohlo stat, ze jsi prepsal treba dnesni datum, pridelenou IP adresu a cestu k aktualnimu adresari nejakyma hovadinama - no dnes se ti az tak uzasne veci spis nepovedou a pruvodci=OS te proste vykopne z vlaku=ukonci ti program uprostred s nejakou divnou chybou. Ale klidne muzes prepsat neco svojeho, nebo neco lokalnejsiho a mene hlidaneho ...)