Ukazatele na struktury

V minulém cvičení změnila funkce prodej_kus jen svou kopii produktu. Řešení znáte z lekce o ukazatelích: předejte funkci adresu proměnné a funkce přes ni změní originál. U struktur to funguje stejně:

typedef struct {
    char nazev[20];
    double cena;
    int pocet;
} Produkt;

void pridej_kusy(Produkt *polozka, int navic) {
    (*polozka).pocet += navic;
}

Parametr polozka je ukazatel na Produkt. *polozka je produkt, na který ukazuje, a (*polozka).pocet je počet kusů tohoto produktu. Závorky jsou nutné: tečka se vyhodnocuje dřív než hvězdička, takže *polozka.pocet by znamenalo „člen pocet proměnné polozka a potom *“, jenže polozka je ukazatel, ne struktura. Proto má C kratší a přehlednější zápis, šipku ->, která se píše jako mínus a znak „větší než“:

#include <stdio.h>

typedef struct {
    char nazev[20];
    double cena;
    int pocet;
} Produkt;

void pridej_kusy(Produkt *polozka, int navic) {
    polozka->pocet += navic;
}

int main(void) {
    Produkt mleko = {"Mléko", 24.9, 3};
    pridej_kusy(&mleko, 2);
    printf("%s: %d\n", mleko.nazev, mleko.pocet);
    return 0;
}

Program vypíše:

Mléko: 5

polozka->pocet znamená přesně totéž co (*polozka).pocet: „člen pocet struktury, na kterou ukazuje polozka“. V praxi se píše šipka a budeme ji psát i my. Funkci voláte s adresou produktu, &mleko, jako u každého parametru, který je ukazatel.

Kdy tedy psát tečku a kdy šipku? Podívejte se, co stojí vlevo. Za strukturou (mleko, kosik[i] nebo parametr Produkt polozka) píšete tečku. Za ukazatelem na strukturu (parametr Produkt *polozka) píšete šipku. Když je spletete, překladač vás upozorní a správnou možnost vám i poradí:

main.c:10:12: error: member reference type 'Produkt *' is a pointer; did you mean to use '->'?
   10 |     polozka.pocet += navic;
      |     ~~~~~~~^
      |            ->

Hláška říká, že Produkt * je ukazatel, a ptá se, jestli jste nechtěli použít ->. Obráceně skončí mleko->pocet chybou member reference type 'Produkt' is not a pointer; did you mean to use '.'?. A když ve volání zapomenete &, tedy pridej_kusy(mleko, 2), hláška zní passing 'Produkt' to parameter of incompatible type 'Produkt *'; take the address with &: funkce chce adresu, a tak jí ji předejte pomocí &.

Ukazatel se hodí, i když funkce strukturu nemění. Parametr typu struktura dostane při každém volání kopii celé struktury a některé struktury jsou velké. Ukazatel je jen jedna adresa. Když funkce strukturu jen čte, přidejte const, stejně jako u polí: ve funkci double cena_polozky(const Produkt *polozka) můžete číst polozka->cena, ale omylem ji nezměníte.

A co pole struktur? Funkce dostane, stejně jako u každého pole, přímo pole volajícího, ne jeho kopii. Ve funkci s parametrem Produkt kosik[] je kosik[i] struktura, a proto píšete tečku, kosik[i].pocet = 0;, a změna v poli volajícího zůstane. Když chcete funkci pridej_kusy předat jeden prvek pole, předejte jí jeho adresu: pridej_kusy(&kosik[1], 2);.

Pokyny

Funkce zapis_vyhru má týmu za výhru přičíst 3 body, ale program vypíše Lvi: 5 bodů. Změňte funkci tak, aby dostala ukazatel na tým, použijte v ní -> a zavolejte ji s adresou proměnné lvi. Program pak vypíše Lvi: 8 bodů.

Pod zapis_vyhru napište funkci void zapis_zapas(Tym *tym, int vstrelene, int obdrzene) pro libovolný zápas. Když tým vstřelil víc gólů, než dostal, vyhrál a získá 3 body, za remízu (stejný počet gólů) získá 1 bod a za prohru nic. Na konci main (před return 0;) ji zavolejte dvakrát, zapis_zapas(&lvi, 2, 2) a zapis_zapas(&lvi, 3, 1), a pak tým znovu vypište stejným printf. Poslední řádek bude Lvi: 12 bodů.

Začněte programovat zdarma

Registrací souhlasíte s provozními podmínkami a zásadami ochrany osobních údajů.

Nebo se registrujte pomocí:

7/9