(tak toto bude dlhý príspevok : D)
Skúsim napísať, ako by som spojáky vysvetľoval ja. (programátori, ignorujte malé nepresnosti)
Čo je štruktúra: Štruktúra je doslova štruktúra nejakého úseku pamäte. To by si mal mať v malíku, nie je ťažké to pochopiť zo zdrojov na internete a z príspevku to nevyzerá, že by si so štruktúrami ako takými mal problém.
Takže spojový zoznam:
Spojový zoznam sa dá predstaviť ako pole prvkov, ale s jednou výhodou naviac - je to dynamické pole, ktoré na svoj život nevyžaduje súvislý blok pamäte. Keď naalokuješ pole trebárs pre 100 čísel int, v pamäti sa ti vyhradí skutočne 400 bajtov za sebou. (za predpokladu, že int má 4 bajty, je to 100*4 bajtov)
Takéto pole vznikne napr takto
alebo ako sa to správne píše
Týmto máš v pamäti súvislý 400 bajtový blok a ukazateľ
pole ti ukazuje na jeho počiatok. Ukazateľ
pole je typu int*, takže počítač vie, že je to ukazateľ na blok pamäte, kde sú za sebou inty a tie majú 4 bajty každý.
No fasa a čo keď žijem v roku 2 a nemám 400 bajtový súvislý blok pamäte? Ako to vyriešiť?

No celkom by sa mi hodilo, keby som mohol mať jednotlivé prvky (čísla) porozhadzované po pamäti a tak by som nepotreboval tak obrovský súvislý blok pamäte. Lenže ako to urobiť? Môžem si postupne alokovať číslo po čísle, ale kde budem ukladať adresy na ne? Do pola? To budem mať pole 100 adries na 100 čísel, to si moc nepomôžem...
No tak inak. Čo keby si každé číslo so sebou nieslo aj adresu nasledujúceho prvku, ktorý je o index vyššie? Tak by mi stačilo uložiť číslo a ešte k nemu jednu adresu, to by mohlo byť len 8 bajtov (4 číslo + 4 adresa). To je celkom dobré, 8 bajtové súvislé bloky v pamäti mám a je ich kopa.
Tak si teda vytvorím číslo, ktoré si okrem svojej hodnoty nesie pri sebe aj adresu na ďalší prvok. Na to sa mi hodí štruktúra, tak hurá na ňu.
Kód: Vybrať všetko
struct moja_struktura_t
{
int cislo;
hmm... // co sem?
}
Lenže ako napíšem adresu nasledujúceho prvku? No ono to v podstate môže byť akákoľvek adresa, pretože ja ako múdry programátor viem, že bude ukazovať na 8 bajtovú štruktúru s jedným číslom a adresou za ním. Tak teda takto?
Kód: Vybrať všetko
struct moja_struktura_t
{
int cislo;
void * dalsi_prvok;
}
Týmto už mám to, čo som potreboval. Je tam
nejaká adresa
niekam do pamäte, kde ale ja viem, že je moja štruktúra. Môžem si teda kľudne vyrobiť svoje pole
Kód: Vybrať všetko
moja_struktura_t * n1 = malloc( sizeof(moja_struktura_t) );
n1->cislo = 42;
n1->dalsi_prvok = malloc( sizeof(moja_struktura_t) );
n1->dalsi_prvok->cislo = /// pockat, to nebude fungovat, ta adresa je void :/ Tak najprv si to musim previest, ach jo
moja_struktura * n2 = (moja_struktura_t *) n1->dalsi_prvok;
n2->cislo = 84;
n2->dalis_prvok = malloc( sizeof(moja_struktura_t) );
...
Tak toto by už fungovalo. A mám svoje pole.
Lenže to škaredé pretypovanie sa mi tam nepáči. Nemôže počítač vedieť, že to je adresa na moja_struktura_t? No jasné, že môže, jednoducho
Kód: Vybrať všetko
struct moja_struktura_t
{
int cislo;
moja_struktura_t * dalsi_prvok;
}
To je validný zápis, pretože počítač vie, koľko bajtov zaberá takáto štruktúra (4 bajty na int a 4 bajty na nejakú adresu)
Takže už sa môžem zbaviť toho hlúpeho pretypovania a pôjde mi to krásne, trebárs aj takto
Kód: Vybrať všetko
n1->dalsi_prvok->dalsi_prvok->dalsi_prvok->dalsi_prvok->cislo = 1024;
n1->dalsi_prvok->dalsi_prvok->dalsi_prvok->dalsi_prvok->dalsi_prvok = malloc...;
V poli by som toto mal zapísané ako
pole[4] = 1024 (napĺňam štvrtý prvok môjho "pola", ktorý je ale vo forme spojáku)
Ňo. S touto teóriu sa môžem pustiť do tvorby spojáku. Žiaľ, naplnenie spojáku nie je tak triviálne, ako naplnenie pola. Urobím si teda pomocné funkcie, ktoré mi pomôžu pracovať s tým podobne pohodlne, ako s polom.
Ako prvé teda chcem pridat nejaký prvok na koniec spojáku, to sa hodí pre jeho naplnenie.
Blbá otázka: ako viem, kde je koniec spojáku? Keď budem skákať z prvku na prvok, kde sa mám zastaviť? Jednoduché, proste adresu ďalšieho prvku nastavím na NULL

Takže funkcia, ktorá mi pridá číslo na koniec môjho "pola", hlúpa verzia číslo 1:
Kód: Vybrať všetko
void pridajCislo( moja_struktura_t * zoznam, int pcislo ) // zoznam je prvy prvok spojaku, pcislo chcem pridat na koniec.
{
if( zoznam == null ) // ziadny prvy prvok este neexistuje
{
zoznam = malloc...
zoznam->cislo = pcislo;
zoznam->dalsi_prvok = NULL; // nastavim ako posledny prvok
return;
}
if( zoznam->dalsi_prvok == null )
{
zoznam->dalsi_prvok = malloc...
zoznam->dalsi_prvok->cislo = pcislo;
zoznam->dalsi_prvok = NULL;
return;
}
if( zoznam->dalsi_prvok->dalsi_prvok == null )
{
zoznam->dalsi_prvok->dalsi_prvok = malloc...
zoznam->dalsi_prvok->dalsi_prvok->cislo = pcislo;
zoznam->dalsi_prvok->dalsi_prvok = NULL;
return;
}
if( zoznam->dalsi_prvok->dalsi_prvok->dalsi_prvok == null )
{
zoznam->dalsi_prvok->dalsi_prvok->dalsi_prvok = malloc...
zoznam->dalsi_prvok->dalsi_prvok->dalsi_prvok->cislo = pcislo;
zoznam->dalsi_prvok->dalsi_prvok->dalsi_prvok = NULL;
return;
}
...
}
a tak ďalej, kde to má konca? Kým nedojdem na 100? Uf, to je nejaké divné

Chcelo by to nejaké jednoduchšie riešenie. Vyzerá, že by sa tam dal použiť nejaký cyklus a v ňom furt skákať na ďalší prvok, kým môžem... to vyzerá celkom reálne, tak teda niečo ako
Kód: Vybrať všetko
void pridajCislo( moja_struktura_t * zoznam, int pcislo )
{
while( zoznam->dalsi_prvok != NULL )
zoznam = zoznam->dalsi_prvok;
// a tu by som mal byt na konci
zoznam->dalsi_prvok = malloc...
zoznam->dalsi_prvok->cislo = pcislo;
zoznam->dalsi_prvok->dalsi_prvok = NULL;
}
Toto už vyzerá krajšie a zvládne to aj 100 prvkov ľavou-zadnou. Len to má ešte pár múch - čo ak bude zoznam null?(Program spadne) Ďalej, pri nepozornosti by sa mohlo stať, že stratím adresu úplne prvého prvku. Čo sa stane ak si náhodou niekde prepíšem túto adresu?(Už sa k nej nikdy nedostanem a som v riti)
A vôbec, celé to má pár múch, ale ak pochopíš toto, tak by si ich mal byť schopný vychytať. A napísať to správne. Skús a keď tak, napíš. A skús si to nakresliť

(hlavne tie dalsi_prvok->dalsi_prvok->dalsi_prvok->dalsi_prvok...) Hlavné je vidieť to v súvislosti s pamäťou (ktorá je organizovaná po bajtoch) a s tým suvisiace pointre(adresy).