Anketa Študent roka

11th Mar 2024

Naučíte sa

  • Pracovať poľom štruktúr.
  • Pracovať s operátorom ->.
  • Dynamicky pretypovať smerník pomocou operátora ().
  • Vytvoriť porovnávaciu funkciu.
  • Triediť pomocou funkcie qsort.

Časopis Emmma vypísal každoročnú anketu v ktorej čitatelia určia "Emmma Študent roka 2021". Do súťaže sa môže prihlásiť ktokoľvek! Našou úlohou bude pomôcť pri sčítavaní hlasov.

Navrhneme jednoduchú databázu mien, v ktorej budeme ku každému menu evidovať počet hlasov, ktoré študent dostal od čitateľov časopisu Emmma.

Meno a počet hlasov bude uvedený na jednom riadku a bude oddelený medzerou.

Vaša aplikácia by mala spočítať všetky hlasy pre každého študenta a výsledky zotriediť podľa podľa počtu hlasov. V prípade, že viac študentov má rovnaký počet hlasov tak ich zotrieďte podľa abecedy (lexikograficky).

Naštudujte si

Využijete tieto časti štandardnej knižnice:

  • fgets() na načítanie jedného riadka,
  • qsort() na triedenie,
  • strcmp() na lexikografické porovnanie dvoch reťazcov,
  • memcpy() na kopírovanie ľubovoľného poľa alebo reťazca.
  • strtol() na premenu reťazca na celé číslo a na zistenie konca čísla.
  • strlen() na zisťovanie dĺžky reťazca.
  • memset() na inicializáciu pamäte.

Pozrite si predpis funkcie, návratovú hodnotu, spôsob hlásenia chybových statvov, opis argumentov a čo funkcia robí.

Analýza riešenia

Definujeme si dátový typ štruktúra pre uloženie jednej položky databázy.

Jeden študent je definovaný pomocou:

  • mena (reťazec),
  • počtu hlasov (celé číslo).

To si vieme vyjadriť napríklad pomocou takejto štruktúry:

#define SIZE 100

struct student {
    char name[SIZE];
    int votes;
};

Konštanta SIZE nám pomôže pri práci s reťazcami a poliami.

Databáza bude tvorená poľom štruktúr konštantnej veľkosti, ktoré si vyhradíme na začiatku.

Budeme potrebovať aj premennú na uloženie aktuálneho počtu študentov v databáze (to nie je to isté ako maximálny počet ktorý sa zmestí do poľa). Nikdy nesmieme zabudnúť na inicializáciu pamäte:

struct student databaza[SIZE];
memset(databaza,0,SIZE*sizeof(struct student));
int size = 0;

Takto si vyhradíme miesto v pamäti pre maximálne 100 študentov a poznačíme si, že momentálne nemáme zapísaný žiadny záznam.

Načítanie jednej položky do databázy

Jeden záznam v databáze sa nachádza na jednom riadku.

Prvou úlohou bude premeniť reťazec zo štandardného vstupu na celé číslo a meno.

Načítajte celý riadok zo štandardného vstupu do pomocného poľa a overte, či ste ho načítali správne:

char line[SIZE];
memset(line,0,SIZE);
char* r = fgets(line,SIZE,stdin);
if (r == NULL){
    // Nastal koniec načítania
}

Ak sa načítanie podarilo, v poli by sme mali mať celé číslo a meno oddelené medzerou. Za menom sa nachádza aj znak pre koniec riadka.

V pamäti by tom malo vyzerať nejako takto (ak Janko dostal od niekoho 10 hlasov a pole line má 11 miest):

|<--------------line------------------------>|
+---+---+---+---+---+---+---+---+----+---+---+
| 1 | 0 |   | J | a | n | k | o | \n | 0 | 0 |
+---+---+---+---+---+---+---+---+----+---+---+

|<----->|    |<---------------->|
  počet              meno

Našou úlohou bude v reťazci nájsť počet hlasov a meno.

Načítanie celého čísla

Pri určovaní počtu hlasov nám pomôže funkcia strtol() ktorá premení reťazec cifier na číslo a zároveň do smerníkovej premennej zapíše adresu prvého znaku ktorý nie je celé číslo. To využijeme pri hľadaní mena za číslom.

char* end = NULL;
int value = strtol(line,&end,10);
if (value == 0){
    // Premena sa nepodarila
}

Ak sa konverzia podarila, tak v premennej end bude adresa miesta, kde môžeme pokračovať v hľadaní mena.

Hodnota end bude ukazovať na medzeru za číslom:

 line    end
  |       |
  v       V
+---+---+---+---+---+---+---+---+----+---+---+
| 1 | 0 |   | J | a | n | k | o | \n | 0 | 0 |
+---+---+---+---+---+---+---+---+----+---+---+

Načíanie mena

Na uloženie mena bude dobré si vyhradiť ďalšie pomocné pole do ktorého nakopírujeme zvyšok reťazca. Adresa v pamäti je zároveň aj začiatok poľa, takže zistiť meno bude jednoduché pomocou smerníkovej aritmetiky. Treba len dávať pozor na to, že koniec riadka nie je súčasťou mena.

Môžme to urobiť napríklad takto:

// pomocné pole
char name[SIZE];
// vynulujeme
memset(name,0,SIZE);
// Vypocitame zaciatok mena
// Jedno miesto za medzerou
char* zaciatok_mena = end + 1;
// Velkost mena je pocet znakov do konca retazca
// minus koniec riadka
int velkost_mena = strlen(zaciatok_mena) - 1;
if (velkost_mena > 0){
    // nakopirujeme
    memcpy(name,zaciatok_mena,velkost_mena);
    // Na konci je v poli name ulozeny retazec s menom
    // bez konca riadka a s nulou na konci
}
else {
    // nepodarilo sa nacitat meno
}

Vyhľadanie položky v databáze

Pri spočítavaní si najprv musíme overiť, či záznam v databáze existuje alebo nie.

Ak sa meno človeka v databáze nenachádza, vložíme ho do prvého voľného miesta v databáze. Ak sa meno človeka v databáze nachádza, zväčšíme jeho počet hlasov.

Na vyhľadanie mena v databáze si vytvorte funkciu, v ktorej prejdete všetky položky v databáze a ak nájdete položku so zadaným menom tak vrátite jej index.

Inak vrátte -1.

Funkcia na vyhľadanie môže vyzerať nejako takto:

int find_student(struct student* students,int size, const char* name){
    // Dopíšte cyklus, ktorý prejde všetky položky v databáze
    // a ak nájde zhodné meno tak vráti jeho index.
    return -1;
}

Pridanie položky do databázy

Do pomocnej premennej si uložte index, kde sa nachádza položka databázy. Podľa indexu budete vedieť s ktorým miestom v poli manipulujete. Pre prístup k členom štruktúry v poli použite operátor . (bodka).

Ak sa položka v databáze nenachádza, nastavte index na prvé voľné miesto v poli a zväčšite počítadlo počtu prvkov v poli. Do prvého voľného miesta zapíšte novú položku databázy.

Pozor - reťazce sa nedajú kopírovať pomocou =, musíte použiť memcpy alebo strcpy. Ak použijete memcpy tak dávajte pozor, aby bol výsledný reťazec zakončený nulou.

int id = find_student(databaza,size,name);
if (id< 0){
    // Skopirujte zaznam na posledne miesto v poli
    memcpy(databaza.name,name,velkost_mena);
    // Zvacsite pocet zaznamov 
    size+=1;
}
else {
    // pripocitajte pocet hlasov
}

Triedenie

Po konci načítania zotrieďte databázu podľa počtu získaných hlasov a zobrazte jej obsah.

Na zotriedenie databázy použite štandardnú funkciu qsort() na triedenie.

Na správne zotriedenie je potrebné definovať funkciu na porovnanie dvoch hodnôt, ktoré dostanete adresu. Názov tejto funkcie uveďte ako posledný argument do funkcie qsort.

Funkcia pre porovnanie má vždy rovnaký predpis, ale dá sa použiť na triedenie ľubovoľného poľa.

int compare(const void* p1, const void* p2);

Pre správne porovnanie dvoch záznamov je potrebné zmeniť typ smerníka na typ s ktorým pracujeme. Vo vašej funkcii pre porovnanie zmeňte typ (const void* na (struct student*). Potom s pomocou operátora -> získate počet hlasov alebo meno na porovnanie.

int compare(const void* p1, const void* p2){
    struct student* s1 = (struct student*)p1;
    struct student* s2 = (struct student*)p2;
    // s1->votes je počet hlasov
    // s1->name je meno študenta
    return 0;
}

Odovzdanie

Do Vášho GIT repozitára do súboru program.c v adresári du5 cez systém Traktor.

Previous Post Next Post

Anketa Študent roka