Labor, 10. hét: láncolt listák II.
Czirkos Zoltán, Pohl László · 2019.07.30.
Láncolt listákkal és fájlkezeléssel kapcsolatos gyakorlófeladatok régebbi ZH-kból.
Ez a labor új anyagot nem mutat be. A feladatok régebbi ZH-kból származnak. Hasonlók az idei második NZH-ban is lehetnek. Ha úgy érzed, ez már jól megy, helyette dolgozhatsz a nagy házidon is.
Felkészülés a laborra:
- A listákról szóló előadás átismétlése.
- A fájlkezelésről szóló előadásrészlet átismétlése.
A nagy házihoz adunk segédletként egy Debugmalloc nevű programot, amely a memóriakezelési hibák egy részét képes fölfedezni. Erről bővebb leírást a Segédlet/Nagy házi menüpont alatt találsz: Debugmalloc. A mostani laborfeladat ennek az eszköznek a kipróbálása.
- Hozz létre egy projektet a szokásos módon a Code::Blocksban (File, New project stb.)
- Töltsd le az alábbi két fájlt: debugmalloc.h és
debugmalloc-impl.h.
Másold be ezeket a projekted mappájába, a main.c fájl mellé:
- Add hozzá ezeket a fájlokat a projektedhez. Ehhez előbb kattints jobb gombbal a projekt nevére
a management ablakban:
- Aztán válaszd ki a fájlokat, és kattints az okéra! Ellenőrizd, hogy a fájlok tényleg be fognak-e
kerülni a programba (Debug és Release be van-e jelölve):
- Végül ehhez kell jutnod:
Ha kész vagy, a helló világ programod írd felül az alábbi forráskóddal! Ez kettő memóriakezelési hibát tartalmaz. Látod, melyek ezek? Ha igen, akkor se javítsd még ki őket!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "debugmalloc.h"
int main(void) {
char txt[] = "hello";
char *p = malloc(strlen(txt) * sizeof(char));
strcpy(p, txt);
printf("TXT: %s\n", p);
return 0;
}
Futtasd le a programot, és figyeld meg a Debugmalloc hibaüzeneteit! Javítsd ki a hibákat!
Szöveges fájlokban metrójáratok megállóinak neveit tároljuk, soronként egyet. A nevek maximum 50 betűsek. A programod feladata, hogy láncolt listákba beolvassa a járatok adatait, és megmondja két adott járatról, hogy át lehet-e szállni egyikről a másikra, vagy nem; ha igen, akkor az is kérdés, hogy hol.
- Írj függvényt, amely paraméterként egy fájlnevet kap, visszatérési értéke pedig egy járat megállóinak listája – a fájlban szereplő sorrendben.
- Írj függvényt, amely megvizsgál két, paraméterként kapott megállólistát, hogy van-e átszállási lehetőség (azonos megállónév). A függvény visszatérési értéke egy sztring legyen, amely a megálló neve. (Mivel kell visszatérnie a függvénynek, ha nem lehet átszállni?)
- Egészítsd ki mindezt teljes programmá, amelyben beolvasod az m2.txt és az m3.txt nevű fájlokat! Ha át lehet szállni, írd ki a megálló nevét, ha nem, akkor pedig írd ki azt! Ne felejtsd el felszabadítani a memóriát, amit foglaltál!
Adatok a teszteléshez: m1.txt, m2.txt, m3.txt és m4.txt.
Írj programot, amelyik tankörszámokat, NEPTUN kódokat, neveket és átlagokat tárol:
13 MZPERX 5.1 Köb Üki
Ez azt jelenti, hogy Köb Üki a 13-as tankörbe jár, MZPERX a kódja és 5.1-es az ösztöndíjátlaga. Írd meg a programot úgy, hogy fájlt olvasson be! Teszteléshez minta: nevsor-latin2.txt (Latin2, Windows), illetve nevsor-utf8.txt (UTF-8, Linux). Válaszd az alábbi adatszerkezetek valamelyikét:
- Egyszerűbb változat: építs egyszeresen láncolt listát az adatokból!
- Nehezebb változat: építs fésűs listát! Ebben a „külső” lista a tankörök listája, azokon belül pedig a „belső” listák a hallgatókat tartalmazzák elemekként.
Ellenőrzésként írasd ki az adatokat a konzolra! Végül pedig ne feledkezz meg az adatszerkezet felszabadításáról sem!
Megoldás
A megoldásban egy fésűs listát kell alkalmazni: kívül a tankörök listája, és mindegyik tankör tartalmaz egy névsort (hallgatók listája).
Minden beolvasott hallgatónál előbb meg kell keresni a tankört (vagy létrehozni, ha még nincs), és utána abba a listába betenni. Mivel a feladat nem határozza meg a sorrendet, egyszerűen az elejére szúrjuk be az adatokat.
#include <stdio.h>
#include <stdlib.h>
/* egy hallgato adatai - lancolt listaban */
typedef struct Hallgato {
char neptun[6 + 1]; /* a neptun kodja, max 6 */
char nev[30 + 1]; /* a neve, max 30 */
double atlag; /* az atlaga */
struct Hallgato *kov;
} Hallgato;
/* egy tankor adatai: egy nevsor */
typedef struct Tankor {
int szam; /* sorszama */
Hallgato *nevsor; /* nevek */
struct Tankor *kov;
} Tankor;
/* beolvassa a szabvanyos bemenetrol a sorokat.
* visszater egy uj Tankor listaval. */
Tankor *tklista_beolvas(void) {
Tankor *tklista = NULL; /* ez lesz a kimenet */
Hallgato temp; /* a beolvasashoz */
int tk;
/* %[^\n] - sztring enterig */
while (scanf("%d %s %lf %[^\n]", &tk,
temp.neptun, &temp.atlag, temp.nev) == 4) {
/* meglevo tankor keresese */
Tankor *iter = tklista;
while (iter != NULL && iter->szam != tk)
iter = iter->kov;
/* ha null, nem lett meg, uj kell */
if (iter == NULL) {
iter = (Tankor*) malloc(sizeof(Tankor));
iter->szam = tk;
iter->kov = tklista;
iter->nevsor = NULL;
tklista = iter; /* elejere */
}
Hallgato *ujh = (Hallgato*) malloc(sizeof(Hallgato));
*ujh = temp; /* minden adatot bemasolunk! */
ujh->kov = iter->nevsor;
iter->nevsor = ujh; /* elejere */
}
return tklista;
}
/* kilistazza a tankort es a nevsorokat. */
void tklista_listaz(Tankor *tklista) {
Tankor *tkiter; /* tankorok iteratora */
Hallgato *hgiter; /* hallgatok iteratora */
for (tkiter = tklista; tkiter != NULL; tkiter = tkiter->kov) {
printf("%2d. tankor -- \n", tkiter->szam);
for (hgiter = tkiter->nevsor; hgiter != NULL; hgiter = hgiter->kov)
printf("%6s %-30s %g\n", hgiter->neptun, hgiter->nev, hgiter->atlag);
}
}
void tklista_felszabadit(Tankor *tklista) {
while (tklista != NULL) {
Tankor *kov = tklista->kov; /* kimentjuk */
Hallgato *nevsor = tklista->nevsor;
while (nevsor != NULL) {
Hallgato *kov = nevsor->kov; /* kimentjuk */
free(nevsor);
nevsor = kov;
}
free(tklista);
tklista = kov;
}
}
int main(void) {
Tankor *tklista = tklista_beolvas();
tklista_listaz(tklista);
tklista_felszabadit(tklista);
return 0;
}
Egy hétemeletes szállodában a szobafoglalásokat egy olyan struktúrában tárolják, amely egy egészként tartalmazza a szobaszámot és egy maximum 50 karakteres sztringben a vendég nevét. A szobák a szokásos módon vannak számozva, a százasok helyiértéke adja meg az emeletet, a tízesek és egyesek pedig az adott szinten lévő szoba sorszámát (pl. 712 = 7. emelet, 12. szoba). A földszint a 0. szint.
- Írj függvényt, amely a paramétereként megadott nevű szöveges fájlból beolvassa a vendégek listáját egy láncolt listába, és visszatér a lista címével! A fájlban az egyes foglalások külön sorban helyezkednek el, a sor elején a szobaszám áll, utána a név. (Hozz létre ilyen szövegfájlt néhány adattal, hogy tesztelni tudj!)
- Írj függvényt, amely paraméterként kapja a vendégek listáját és a szintek tömbjét; az utóbbi, emelet sorszámával indexelt tömbbe pedig beírja, hogy az egyes emeleteken hány vendég lakik!
- Írj függvényt, amely megkapja az így keletkezett szintek tömbjét, és visszatér a legzsúfoltabb emelet sorszámával, tehát azzal, ahol a legtöbb vendég van éppen!
Egészítsd ki ezeket teljes programmá, amely a felhasználótól beolvas egy nevet és megmondja, hogy az illető megszállt-e a szállóban a foglalasok.txt szerint, és ha igen, akkor a legzsúfoltabb emeleten van-e a szobája!
Megoldás
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct vendeg {
int szobaszam;
char nev[51];
struct vendeg *kov;
} vendeg;
vendeg *beolvas(char const *fajlnev) {
vendeg *lista = NULL, *uj, tmp;
FILE *fajl;
fajl = fopen(fajlnev, "rt");
while (fscanf(fajl, "%d %[^\n]", &tmp.szoba, tmp.nev) == 2) {
uj = (vendeg *) malloc(sizeof(vendeg));
uj->szobaszam = tmp.szoba;
strcpy(uj->nev, tmp.nev); /* v. *uj = tmp */
uj->kov = lista;
lista = uj;
}
fclose(fajl);
return lista;
}
void betoltottseg(vendeg *lista, int *szintek) {
vendeg *iter;
int i;
for (i = 0; i < 8; ++i)
szintek[i] = 0;
for (iter = lista; iter != NULL; iter = iter->kov)
szintek[iter->szobaszam / 100] += 1;
}
int legtobb_vendeg(int *szintek) {
int i, max = 0;
for (i = 1; i < 8; ++i)
if (szintek[i] > szintek[max])
max = i;
return max;
}
vendeg *keres(vendeg *lista, char const *nev) {
vendeg *iter;
for (iter = lista; iter != NULL; iter = iter->kov)
if (strcmp(iter->nev, nev) == 0)
break;
return iter;
}
int main(void) {
char vendegnev[51];
vendeg *lista = beolvas("foglalasok.txt");
scanf("%[^\n]", vendegnev);
vendeg *talalat = keres(lista, vendegnev);
if (talalat == NULL)
printf("A vendég nincs a szállóban.\n");
else {
int szintek[8];
betoltottseg(lista, szintek);
int legzsufoltabb = legtobb_vendeg(szintek);
if (legzsufoltabb == talalat->szobaszam / 100)
printf("A vendég a legzsúfoltabb szinten lakik.\n");
else
printf("A vendég nem a legzsúfoltabb szinten lakik.\n");
}
while (lista != NULL) {
vendeg *temp = lista->kov;
free(lista);
lista = temp;
}
return 0;
}