[C] Spájaný zoznam štruktúr

Programovacie jazyky, rady, poradňa...
Hensym
VIP
VIP
Používateľov profilový obrázok
Príspevky: 6978
Registrovaný: 24 apr 2011, 0:53
Bydlisko: Zvolen

[C] Spájaný zoznam štruktúr

Príspevok od používateľa Hensym »

Ahojte, potrebujem vypracovať projekt, a čas sa kráti. S projektom by som nemal mať problém, no paradoxne, absolútne neviem začať.

Čiže, nebudem si dávať servítku pred ústa, nemôžem sem dať ukážky kódu a napísať, čo konkrétne nefunguje, pretože som ten kód prakticky zatiaľ skoro žiadny nenapísal.

Ide o to, že musím z textového súboru, obsahujúceho záznamy (meno, id preukazu,...) oddelené riadkami načítať do spájaného zoznamu štruktúr, a vôbec netuším, ako na to. Kolega tuto na fore je očividne z rovnakej školy a vypracuváva rovnaký projekt, no ani na základe jeho kódu som to ani trochu nepochopil, a kopírovať kód určite nechcem.

Aby ste si nemysleli, že sa pýtam bez snahy, - komplet celé prednášky o štruktúrach som si prešiel asi 3x, plus, osobitne som prezeral example, alebo alternatívne stránky vysvetľujúce spájané zoznamy, no absolútne tomu nerozumiem.

Vôbec neviem, aké použitie majú tie pointre *prvý,*aktuálny,*ďalší, a podobne - ktoré na seba nejako navzájom odkazujú. Z programkom som zatiaľ problém moc nemal, a prechádzam s dosť veľkým počtom bodov, ale tieto štruktúry idú absolútne mimo mňa, neviem či mám slabé obdobie, alebo čo.

Našla by sa tu nejaká dobrá duša, čo by mi to vedela čo najpodrobnejšie vysvetliť? Tie algoritmy, nie len ukážkami, ale aj slovne - že prečo to je tak, ako to je, aby som to pochopil? Prípadne, SS?

Ak by to bolo na dlhšie, určite sa za tvoj čas odmením karmou, prípadne pár symbolickými eurami.
PS: nežiadam o vypracovanie kódu, aby ste ma nechápali zle. :)

Ďakujem, dosť mi to ponáhľa.
pcsiete
Medium Star
Medium Star
Príspevky: 413
Registrovaný: 07 dec 2012, 18:47

Re: [C] Spájaný zoznam štruktúr

Príspevok od používateľa pcsiete »

...dáš sem aj zadanie? :)

Čo presnejšie nechápeš? Spájané zoznamy, manipuláciu s nimi alebo spôsob načítavania dát?
Hensym
VIP
VIP
Používateľov profilový obrázok
Príspevky: 6978
Registrovaný: 24 apr 2011, 0:53
Bydlisko: Zvolen

Re: [C] Spájaný zoznam štruktúr

Príspevok od používateľa Hensym »

Zadanie sem kľudne môžem dať, myslím, že tým nič nepokazím.
https://mega.co.nz/#!55hnTLpb!0kdzd31tz ... IeRV_ATed4

Každopádne, už som to trošičku pochopil, po prečítaní Heroutovej knihy, no stále nie celkom rozumiem prebiehaniu cez zoznam štruktúr, po načítaní. Teda, načítané mám 3 záznamy (každý po 6 riadkov),... Ak som správne pochopil, vytvoril som akési 3 rôzne štruktúry?

Herout ich potom všetky prebehuje for-cyklom, ktorému nie celkom rozumiem (príloha).
Prílohy
asd.png
BX
Addict
Addict
Používateľov profilový obrázok
Príspevky: 4572
Registrovaný: 10 jan 2008, 15:30

Re: [C] Spájaný zoznam štruktúr

Príspevok od používateľa BX »

(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

Kód: Vybrať všetko

int * pole = malloc( 400 );
alebo ako sa to správne píše

Kód: Vybrať všetko

int * pole = malloc( 100 * sizeof(int) );
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ť? :cry:
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é :D
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).
harrison314
Hardcore addict
Hardcore addict
Používateľov profilový obrázok
Príspevky: 8217
Registrovaný: 27 máj 2009, 20:42
Bydlisko: Bratislava
Kontaktovať používateľa:

Re: [C] Spájaný zoznam štruktúr

Príspevok od používateľa harrison314 »

Ja este doplnim, mne v prvaku pri podobnom projekte velmi pomohlo (a odbremenilo ma od plno ostrovaciek) to, ze som nepouzival pointer na prvu strukturu zoznamu ako pointer na celi zonam, ale spravil som si osobitnu strukturu, kde bol pointer na prvu strukturu zoznamu a este nejake veci napriklad dlzka zoznamu.
Setri to starosti aj hviezdicky :D
pcsiete
Medium Star
Medium Star
Príspevky: 413
Registrovaný: 07 dec 2012, 18:47

Re: [C] Spájaný zoznam štruktúr

Príspevok od používateľa pcsiete »

Hoci už neskôr, pridávam aj svoje vysvetlenie. Dúfam že od BXovho nebude mať zopár múch, ale neskúšal som ho :) vždy sa nejaká nájde.
Vopred sa ospravedlňujem za formátovanie.

Spájaný zoznam (anglicky "linked list") nie je nič iné ako prepojenie pointrov. Z toho vyplýva že každá položka zoznamu bude mať mimo iné minimálne jeden pointer, ktorý bude ukazovať na ďalšiu položku v rade na zachovanie myšlienky zoznamu.

V rámci tohoto príspevku si zadefinujeme nejaké pojmy:
  • zoznam = štruktúra držiacia informáciu o zozname + položky zoznamu
  • položka zoznamu = štruktúra držiaca dáta + iné informácie na sprácu so zoznamom
  • dáta = dáta ktoré chceš ukladať v položkách - ignoruješ teda duplicitu záznamov, podobnosť, kľúčové položky a podobne, sústredíš sa na pamäť
Rozpíšem tu aj zopár základných operácií, konkrétne
  • vytvorenie nového prázdneho zoznamu - ll_new
  • zmazanie celého zoznamu (teda zoznamu a všetkých jeho položiek, ale bez člena data) - ll_destroy
  • získanie dĺžky zoznamu - ll_count
  • nastavenie dát položky - ll_set
  • záskanie dát položky - ll_get
  • pridanie položky - ll_add
  • zmazanie položky - ll_remove
Vytvorenie a zmazanie zoznamu
Často sa stretneš len s jednoduchou štruktúrou spájaného zoznamu, ktorá neobsahuje nič len položky. Každá položka obsahuje nejaké dáta, odkaz na ďalšiu položku, a ak je spájaný zoznam dvojsmerný (čo tento bude) tak aj odkaz na predošlú položku.

Kód: Vybrať všetko

struct ll_entry_s {
	//predošlá a nasledujúca položka
	struct ll_entry_s * prev, next;
	
	//dáta ktoré budeme ukladať
	void * data;
};
typedef ll_entry_s ll_entry, *pll_entry;
Samotný zoznam však môže obsahovať aj iné informácie, či už na dodatočnú manipuláciu s položkamu, na jej zrýchlenie alebo na prácu so samotnými dátami (čokoľvek, priemery hodnôt, pamäťové rozsahy). Tu som vytvoril jednoduchú štruktúru tohoto zoznamu, obsahujúcu len odkaz na začiatok a koniec zoznamu.

Kód: Vybrať všetko

struct linkedlist_s {
	pll_entry first, last;
};
typedef linkedlist_s linkedlist, *plinkedlist;
V prípade, že chcem vytvoriť nový zoznam, potrebujem alokovať novú kópiu tejto prázdnej štruktúry, nič viac.

Kód: Vybrať všetko

plinkedlist ll_new() {
	/* alokuj a vynuluj novú štruktúru pre spájaný zoznam */
	return (plinkedlist)calloc(sizeof(linkedlist));
}
Ak ju chcem vymazať, nestačí mi len odstrániť štruktúru zoznamu ako pri zoznamoch postavených na poliach, ale musím dealokovať aj všetky položky.

Kód: Vybrať všetko

void ll_destroy(plinkedlist ll) {
	pll_entry tmp, curr = ll->first;
	
	//pokým ešte nie je koniec zoznamu
	while(curr!=NULL) {
		//ulož si nasledujúcu položku zoznamu
		tmp = curr->next;
		
		//dealokuj túto položku
		free(curr);
		
		//... a pokračuj ďalšiou položkou
		curr = tmp;
	}
	
	//nakoniec uvoľni aj samotnú štruktúru zoznamu
	free(ll);
}
Tu by som sa rád pozastavil a poznamenal, že to ako spoznáš či si na konci zoznamu záleží od jeho druhu. Niektoré zoznamy môžu byť zakončené neplatnými dáatami. Niektoré môžu byť s použitím návrhového vzoru Null Object ukončené prázdnym objektom bez funkcionality, no s nárokmi na pamäť. Pri kruhových zoznamoch (posledná položka zoznamu ukazuje znova na prvú) zase pozeráš na to, či sa ti už neopakuje odkaz na prvú položku.

Manipulácia s dátami v zozname
Dĺžka zoznamu sa získava najľahšie. Jednoducho počítaš položky pokým nenájdeš jeho koniec.

Kód: Vybrať všetko

int ll_count(plinkedlist ll) {
	int i = 0;
	pll_entry curr = ll->first;
	
	//sme na konci
	while(curr!=NULL) {
		//...nie
		curr = curr->next;
		
		//tak pridaj
		i++;
	}
	
	return i;	
}
V celom tomto príklade sú dáta položky uložené ako void * data, čiže môžu ukazovať na ľubovoľnú vec. Tým že sú uložené pod jedným pointrom môžeme z celej implementácie spájaného zoznamu pred užívateľom skryť dôkazy o existencii nejakých položiek. Jediné, s čím sa bude pracovať sú indexy a pointre na dáta.

Pri získavaní položky prechádzaš zoznamom pokým nenájdeš n-tú položku. Ak nájdeš koniec zoznamu predtým než sa dostaneš na položku ktorú chceš spracovať, musíš vrátiť neplatnú hodnotu a/alebo vyhodiť výnimku/chybu.

Kód: Vybrať všetko

int ll_count(plinkedlist ll) {
	int i = 0;
	pll_entry curr = ll->first;
	
	//sme na konci
	while(curr!=NULL) {
		//...nie
		curr = curr->next;
		
		//tak pridaj
		i++;
	}
	
	return i;	
}
void * ll_get(plinkedlist ll, int index) {
	int i = 0;
	pll_entry curr = ll->first;
	
	//pokým niesme na konci
	while(curr!=NULL) {
	
		//ak sme našli položku ktorú hľadáme
		if(i==index)
			//môžeme vrátiť pointer na jej dáta
			return curr->data;
		
		//inak ideme ďalej
		curr = curr->next;
		i++;
	}
	
	//našli sme koniec zoznamu, no nedosiahli sme požadovaný index
	//vraciame neplatnú hodnotu (0xffffffff na 32-bitových systémoch)
	return (void*)-1;
}
Nastavenie dáta položky zoznamu je o tom istom, len nevraciame dáta (tie meníme), ale kontrolnú hodnotu (nezáporný index ak sme uspeli, zápornú hodnotu ak nie).

Kód: Vybrať všetko

int ll_set(plinkedlist ll, int index, void * data) {
	int i = 0;
	pll_entry curr = ll->first;
	
	//ešte niesme na konci?
	while(curr!=NULL) {
		//je toto položka ktorú cheme zmeniť?
		if(i==index) {
			//zmeňme ju
			curr->data = data;
			
			//vráťme index
			return index;
		}
		
		curr = curr->next;
		i++;
	}
	
	//nedosiahli sme potrebný index
	return (int)-1;
}
Manipulácia s položkami v zozname
Položky môžeme pridávať alebo mazať. Druhov pridávania je niekoľko (nahradzovanie, vtĺačanie na index (insert), pridávanie na koniec, pridávanie na začiatok). Pridávať rovnako ako odstraňovť môžeš znova jednu položku alebo zoznam položiek. Zatiaľ ukážem len pridávanie jednej položky na koniec zoznamu a odoberanie položky z ľubovoľného indexu.

Pridávanie je jednoduché. Nájdeš si koniec zoznamu a pridáš element, pričom nesmieš zabudnúť na ukončenie.

Kód: Vybrať všetko

void ll_add(plinkedlist ll, void * data) {
	//alokuj novú položku, ulož adresu na ňu do dočasnej premennej
	pll_entry tmp = (pll_entry)malloc(sizeof(ll_entry));
	
	//nastav dáta položky
	tmp->data = data;
	//predošlým elementom položky na konci zoznamu je bývalý koniec :)
	tmp->prev = ll->last;
	//zoznam ukončíme nulou, NULL
	tmp->next = NULL;
	
	//bývalá posledná položka musí ukazovať na novú
	ll->last->next = tmp;
	//základná štruktúra zoznamu musí tiež ukazovať na novú poslednú položku
	ll->last = tmp;
}
Vymazovanie je zo všetkých možno najzložitejšie. Musíš nájsť položku na danom indexe (čo môže byť aj neúspešné), previazať prepojenia predošlej a nasledujúcej položky navzájom na seba, a až tak môžem dealokovať položku samotnú.

Kód: Vybrať všetko

int ll_remove(plinkedlist ll, int index) {
	int i = 0;
	pll_entry curr = ll->first;
	
	//pokým niesme na konci
	while(curr!=NULL) {
		//našli sme index ktorý hľadáme?
		if(i==index) {
			//ak áno, nasledujúce položka predošlej položky je nasledujúce položka aktuálnej položky
			curr->prev->next = curr->next;
			
			//predošlá položka nasledujúcej položky je predošlá položka aktuálnej položky
			curr->next->prev = curr->prev;
			
			//uvoľni aktuálnu položku
			free(curr);
			
			//vráť nezáporný index
			return index;
		}
		
		curr = curr->next;
		i++;
	}
	
	//v prípade neúspechu vráť zápornú hodnotu
	return (int)-1;
}
Zopár poznámok na záver
Ako si si všimol, vzor na prechádzanie položiek (cyklus while pokým curr==NULL) sa opakuje na viacerých miestach, čo znamená že aj tento vzor by sa dal oddeliť do osobitnej funkcie (spomalenie by bolo malinké).

K jednotlivým operáciam spájaného zoznamu musím dodať, že by mali byť všetky atomické (nerozdeliteľné, neprerušiteľné), aby fungovali aj vo viacerých vláknach naraz. V opačnom prípade je to približne riadky^2 sekúnd odhaľovania chyby :D

Všetky súbory ešte prikladám ako celok (hlavička i súbor), možno sa niekomu zídu...
linkedlist.zip
(1.58 KiB) 122 stiahnutí
Hensym
VIP
VIP
Používateľov profilový obrázok
Príspevky: 6978
Registrovaný: 24 apr 2011, 0:53
Bydlisko: Zvolen

Re: [C] Spájaný zoznam štruktúr

Príspevok od používateľa Hensym »

Bravo. :)

Naozaj obrovské ĎAKUJEM za tieto príspevky - začínam pochybovať o vysokoškolských profesoroch, ak ich 3 hodinové prednášky (ani vo verbálnej ani v digitálnej podobe) ma nie sú schopné naučiť, a vy ste to dali v zopár A4-ách textu.

Ešte by som sa opýtal, pred vypísaním štruktúry potrebujem zistiť, či sú v nej údaje nahrané, a ak nie sú, nevypisujem nič. Štruktúra zrejme "prázdna" nebude, lebo vypisuje divné znaky, - ako by vyzeralo takéto ošetrenie?

Skúšam som if(prvy == NULL) -> čo je prvý záznam štruktúry, no nefungíruje. Je treba ísť cez for cyklus, ako aj pri mazaní celej štruktúry a kontrolovať každú hodnotu zvlášť?

// tak som to vyriešil pomocnou premennou "loaded", ktorá je defaultne na 0, a ak sa vyokoná funkcia načítania, zmení sa na 1. Vo výpise teda iba kontrolujem, či loaded nie je 0, a ak je, tak return. Je to mechanické riešenie, ale pre program snáď aj menej náročné, hádam im to nebude vadiť.
BX
Addict
Addict
Používateľov profilový obrázok
Príspevky: 4572
Registrovaný: 10 jan 2008, 15:30

Re: [C] Spájaný zoznam štruktúr

Príspevok od používateľa BX »

Nič ti nebráni napísať niečo ako

Kód: Vybrať všetko

zoznam_t * zoznam = NULL;

if( zoznam == NULL )
     zoznam je prazdny;

nahraj(zoznam);
if( zoznam == NULL ) // toto uz by sa vykonat nemalo. Iba ak sa vyskytla chyba a vtedy by funkcia nahraj nemala so zoznamom nic urobit (necha ho na NULL)
     zoznam je prazdny;
Ale pomocná premenná loaded nie je zlá, prečo nie...

Inak, ak zoznam pri vytvorení nenastavíš na NULL, tak nadobudne nejakú náhodnú hodnotu (adresu) a niekam ukazovať bude. Čo tam ale je, to nikto nevie.
Len tak pre zábavu, so štruktúrami v C-čku je veľa srandy, napríklad si zoberme takýto kód

Kód: Vybrať všetko

typedef struct
{
    int a;
    int b;
    char c;
    char c2;
} zoznam_t;

int main()
{
    zoznam_t * zoznam2 = malloc( sizeof(zoznam_t) );
    printf( "%p\n", zoznam2 ); // adresa v pamati, kde je struktura ulozena
    zoznam2->a = 42;
    zoznam2->b = 43;
    zoznam2->c = 'B';
    zoznam2->c2 = 'X';
    free(zoznam2);

    zoznam_t * zoznam = malloc( sizeof(zoznam_t) );
    printf( "%p\n", zoznam ); // fiha!

    printf( "%d\n", zoznam->a );
    printf( "%d\n", zoznam->b );
    printf( "%c\n", zoznam->c );
    printf( "%c\n", zoznam->c2 );

    return 0;
}
Väčšinou ti počítač do zoznam pridelí rovnakú adresu (tam, kde je fiha!), ako mi to dalo na predošlú štruktúru, ktorú som ale uvoľnil pomocou free.
Toto je častý zdroj chýb - hlavne v takýchto programoch, ktoré štruktúry často vytvárajú, čítajú a mažú, pretože sa to zdá, že to funguje a pri tom to nefunguje (resp. funguje to viac-menej náhodou). A tiež je to často zdroj chýb v protokoloch a v podstate všade, kde sa takéto veci robia (viz. napríklad aj teraz slávny heartbleed bug funguje na podobnom princípe)
Napísať odpoveď