IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
Avatar
Martin Russin:14.7.2021 17:08

Ahojte, chcel by som sa prosím opýtať na pár vecí k vytvorenému programu, ktorého časť prikladám nižšie.

#include <stdio.h>
#include <stdlib.h>
#define PREDVOLENA_KAPACITA 10

typedef struct {
    int pocet;
    int kapacita;
    int *pole; // premenná pole je preto ukazovateľ, pretože ukazuje na začiatok adresy
// pamäti, ktorú sme si alokovali vo funkcii vytvor_vektor?
} VEKTOR;

VEKTOR *vytvor_vektor() { // prečo je pred názvom funkcie dereferenčný operátor?
// je to názov funkcie alebo ukazovateľ?
    VEKTOR *p_vektor = (VEKTOR *) malloc(sizeof(VEKTOR));
    p_vektor->kapacita = PREDVOLENA_KAPACITA;
    p_vektor->pocet = 0;
    p_vektor->pole = (int *) malloc(sizeof(int) * p_vektor->kapacita); // ukazovateľ p_vektor
// ukazuje na ukazovateľ pole a ten ukazuje na začiatok adresy alokovanej pamäte?
    return p_vektor; // keďže má táto funkcia dátový typ VEKTOR a nie VOID, tak
// vracia nejakú hodnotu - vracia adresu operátora p_vektor? a teda hodnoty
// štruktúry, na ktorú ukazuje?
}
.
.
.
int main(int argc, char** argv) {
    VEKTOR *p_vektor = vytvor_vektor();
.
.
.
    return (EXIT_SUCCESS);
}

Ďakujem za odpoveď.

Zkusil jsem: ...

Chci docílit: ...

 
Odpovědět
14.7.2021 17:08
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Martin Russin
DarkCoder:14.7.2021 17:52
typedef struct {
    int pocet;
    int kapacita;
    int *pole;
} VEKTOR;

Jedná se o vytvoření nového typu VEKTOR, kde pole slouží jako ukazatel na začátek dynamicky alokované pole vytvořeného pomocí funkce vytvor_vektor().

// prečo je pred názvom funkcie dereferenčný operátor? je to názov funkcie alebo ukazovateľ?

VEKTOR *vytvor_vektor() lépe VEKTOR *vytvor_vektor(vo­id) představuje funkci, která nemá žádné parametry a vrací ukazatel na typ VEKTOR.

// ukazovateľ p_vektor ukazuje na ukazovateľ pole a ten ukazuje na začiatok adresy alokovanej pamäte?

Uvnitř funkce vytvor_vektor() je dynamicky vytvořena proměnná typu VEKTOR a ukazatel na tuto proměnnou je vrácen funkcí a přiřazen ukazatelové proměnné při její inicializaci.

VEKTOR *p_vektor = vytvor_vektor();

Jinými slovy:
Definujeme ukazatel p_vektor typu VEKTOR a inicializujeme ho ukazatelem na začátek dynamicky alokované paměti která je alokovaná uvnitř funkce vytvor_vektor(). Uvnitř funkce je současně vytvořeno pole o požadované kapacitě.

Dvě věci:

  1. Funkce malloc() může selhat, je třeba vždy testovat na úspěšnost přidělení paměti.
  2. Je dobré přidělenou pamět v případě, žeuž ji nepotřebujeme uvolňovat pomocí funkce free().
Nahoru Odpovědět
14.7.2021 17:52
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Odpovídá na DarkCoder
Martin Russin:14.7.2021 19:14

VEKTOR *vytvor_vektor() lépe VEKTOR *vytvor_vektor(vo­id) představuje funkci, která nemá žádné parametry a vrací ukazatel na typ VEKTOR.

Takže ak budem potrebovať, aby nejaká funkcia vracala ukazovateľ na jej typ, tak pred názov danej funkcie dám dereferenčný operátor?

 
Nahoru Odpovědět
14.7.2021 19:14
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Martin Russin
DarkCoder:14.7.2021 19:43

Ano. Je to prosté..

TYP func(void) // funkce vrací hodnotu typu TYP
TYP* func(void) // funkce vrací hodnotu která je ukazatelem na TYP

Ukazatel na typ jako návratová hodnota funkce se používá často. Práce se strukturami, řetězci...

Nahoru Odpovědět
14.7.2021 19:43
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Odpovídá na DarkCoder
Martin Russin:15.7.2021 16:41

Chcel by som sa prosím ešte opýtať na pár vecí k spojovému zoznamu.

#include <stdio.h>
#include <stdlib.h>

typedef struct uzol { // Prečo je v tomto riadku aj názov štruktúry?
    int hodnota;
    struct uzol *p_dalsi; // Chápem, že týmto riadkom sme si definovali ukazovateľ
// na ďalší uzol, ale prečo takýmto kódom? Prečo to nemohlo byť napr. UZOL *p_dalsi;?
} UZOL;

typedef struct {
    int pocet;
    UZOL *p_hlava;
    UZOL *p_koniec;
} ZOZNAM;

ZOZNAM *vytvor_zoznam() {
    ZOZNAM *p_zoznam = (ZOZNAM *) malloc(sizeof (ZOZNAM));
    if(p_zoznam == NULL)
    {
        printf("Chyba - Nedostatok pamäte.");
        exit(1);
    }
    p_zoznam->p_hlava = NULL;
    p_zoznam->p_koniec = NULL;
    return p_zoznam;
}

void uvolnenie_zoznamu(ZOZNAM *p_zoznam) {
    UZOL *p_aktualny = p_zoznam->p_hlava; //
    UZOL *p_stary;
    while (p_aktualny != NULL)
    {
        p_stary = p_aktualny; // tento riadok by som mohol zapísať aj takto?
// UZOL *p_stary = p_aktualny;
        p_aktualny = p_aktualny->p_dalsi; // tento riadok je to isté ako toto?
// p_aktualny = p_aktualny->p_zoznam->p_hlava->p_dalsi;
        free(p_stary); //
    }
    free(p_zoznam);
}

UZOL *pridaj_do_zoznamu(ZOZNAM *p_zoznam, int hodnota) {
    UZOL *p_novy = (UZOL *) malloc(sizeof(UZOL));
    if(p_novy == NULL)
    {
        printf("Chyba - Nedostatok pamäti.");
        exit(1);
    }
    p_novy->p_dalsi = NULL; // tento riadok nastaví ukazovateľ nového uzla na
// ďalší uzol na hodnotu NULL, teda ukazovateľ ďalšieho uzla nebude na nič ukazovať.
// Prečo sme tento kód použili?
    if(p_zoznam->p_koniec != NULL)
    {
        p_zoznam->p_koniec->p_dalsi = p_novy;
        p_zoznam->p_koniec = p_novy;
    }
    else
    {
        p_zoznam->p_hlava = p_novy; //Ak bude platiť táto podmienka, teda v zozname
// nebude ešte žiaden prvok v uzli, touto podmienkou sa do 1. uzla (hlava) a aj do
// 2. uzla (koniec) uloží tá istá hodnota? Ako sa potom prepíše hodnota v 2. uzle?
        p_zoznam->p_koniec = p_novy;
    }
    p_novy->hodnota = hodnota;
    p_zoznam->pocet++;
    return p_novy;
}

int main(int argc, char** argv) {
    ZOZNAM *p_zoznam = vytvor_zoznam();
    int i;
    for(i=0; i<1000; i++)
    {
        pridaj_do_zoznamu(p_zoznam, i);
    }
    UZOL *p_aktualny = p_zoznam->p_hlava;
    while(p_aktualny != NULL)
    {
        printf("%d ", p_aktualny->hodnota);
        p_aktualny = p_aktualny->p_dalsi;
    }
    uvolnenie_zoznamu(p_zoznam);
    return (EXIT_SUCCESS);
}

Ďakujem za odpoveď.

 
Nahoru Odpovědět
15.7.2021 16:41
Avatar
Martin Russin:15.7.2021 18:12
UZOL *pridaj_do_zoznamu(ZOZNAM *p_zoznam, int hodnota) {
    UZOL *p_novy = (UZOL *) malloc(sizeof(UZOL));
    if(p_novy == NULL)
    {
        printf("Chyba - Nedostatok pamäti.");
        exit(1);
    }
    p_novy->p_dalsi = NULL;
    if(p_zoznam->p_koniec != NULL)
    {
        p_zoznam->p_koniec->p_dalsi = p_novy;
        p_zoznam->p_koniec = p_novy;
    }
    else
    {
        p_zoznam->p_hlava = p_novy;
        p_zoznam->p_koniec = p_novy;
    }
    p_novy->hodnota = hodnota;
    p_zoznam->pocet++;
    return p_novy;
// keďže sme použili príkaz na alokáciu pamäte, nemalo by na konci
// tejto funkcie dôjsť k uvoľneniu alokovanej pamäte: free(p_novy)?
 
Nahoru Odpovědět
15.7.2021 18:12
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Martin Russin
DarkCoder:15.7.2021 21:30

Název struktury je v tomto případě důležitý. Prvek struktury je ukazatelem na tuto strukturu. Jelikož nový typ v čase definování prvku struktury není znám, nelze použít typ UZEL, ale je třeba použít název struktury. To je důvod, proč je zde uveden i název struktury.

Pokud se ví počet strukturovych proměnných, není obvyklé název struktury zapotřebí. Je však dobré jej uvádět. Lze tak definovat proměnné kdekoli v programu tak jak je třeba. Dále bývá dobré nejprve definovat strukturu a poté používat typedef. Pomáhá to vytvořit vícero rozdílných názvů typů podobného významu.

Takto by si Radek nemohl zapsat jelikož ukazatelová proměnná typu UZEL je již definovánu výše. Došlo by ke kolizi jmen a program by se nepřeložil. Je třeba mít na paměti jak se zapisují definice s inicializací a jak ostré příkazy.

Ne, řádky mají odlišný význam.

Použití NULL je důležité, díky němu se určuje konec seznamu. V kódu je to vidět jak dochází k testování ukazatele na NULL. Dále je dobré ukazatel který neniinicislizovan nebo na nic neukazuje nastavit na NULL.

Když se přidává nový záznam, hledá se konec seznamu. Jakmile se najde, aktualizuje se ukazatel aktuálně poslední položky seznamu, nápoji se na nový záznam a aktualizují se jeho ukazatele.

Použití funkce free() uvnitř funkce by bylo chybou. Funkce slouží jako alokator a vrací ukazatel na aplikovanou paměť. Tento ukazatel předáváš jinému ukazateli. Pokud bys pamět uvolnil, vrátil bych ukazatel na paměť která už není tvoje, což by byla závažná chyba. Tuto alokovanou paměť uvolňujes přes ukazatel, do kterého přiřazuješ vrácený ukazatel z funkce.

Nahoru Odpovědět
15.7.2021 21:30
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Odpovídá na DarkCoder
Martin Russin:16.7.2021 14:35

Dále bývá dobré nejprve definovat strukturu a poté používat typedef. Pomáhá to vytvořit vícero rozdílných názvů typů podobného významu.

Ako máš na mysli najprv definovať štruktúru a až potom použiť typedef?
.

Funkce slouží jako alokator a vrací ukazatel na aplikovanou paměť.

Funkcia vracia konkrétne ukazovateľ p_zoznam na aplikovanú pamäť, teda na pamäť, ktorú sme si alokovali pre štruktúru UZOL?
.

Tento ukazatel předáváš jinému ukazateli.

Ako konkrétne?
.

Tuto alokovanou paměť uvolňujes přes ukazatel, do kterého přiřazuješ vrácený ukazatel z funkce.

Čiže alokovanou paměť uvolňujem konkrétne cez ukazovateľ p_zoznam, free(p_zoznam)? Týmto príkazom som priradil vracený ukazovateľ z funkce? p_zoznam->p_koniec->p_dalsi = p_novy;
.
.
.
Príkazom free(p_zoznam) uvoľním pamäť, na ktorú ukazuje tento ukazovateľ, správne? Tým zároveň nastavím to, že ukazovatelia p_hlava, p_koniec, p_dalsi a p_novy nebudú už na nič ukazovať?
.
.
.
.
V inom programe (evidencia osôb):

typedef struct {
    char *ulica;
    char *mesto;
    char *psc;
    int popisne_cislo;
} ADRESA;

typedef struct osoba {
    int vek;
    char *jmeno;
    ADRESA adresa;
    struct osoba *p_dalsi;
    struct osoba *p_predchadzajuci;
} OSOBA;

typedef struct {
    int pocet;
    OSOBA *p_hlava;
    OSOBA *p_chvost;
} ZOZNAM;

sa taktiež uvoľňuje alokovaná pamäť pre štruktúru ZOZNAM pomocou dodatočného ukazovateľa p_stary. Avšak v tomto prípade, uvoľňujem pamäť aj pre ukazovateľ ukazovateľa (neviem či som sa korektne vyjadril). Je to pre to, že ukazovateľov ako ulica, mesto, psc, jmeno inicializujeme nejakými hodnotami, teda budú na nejaký konkrétny údaj ukazovať?

void uvolnenie_zoznamu(ZOZNAM *p_zoznam) {
    OSOBA *p_aktualny = p_zoznam->p_hlava;
    OSOBA *p_stary;
    while(p_aktualny != NULL)
    {
        p_stary = p_aktualny;
        p_aktualny = p_aktualny->p_dalsi;
        free(p_stary->jmeno);
        free(p_stary->adresa.mesto);
        free(p_stary->adresa.psc);
        free(p_stary->adresa.ulica);
        free(p_stary);
    }
    free(p_zoznam);
}

Opäť ďakujem za odpoveď.

 
Nahoru Odpovědět
16.7.2021 14:35
Avatar
Martin Russin:16.7.2021 15:22

V tejto štruktúre

typedef struct osoba {
    int vek;
    char *jmeno;
    ADRESA adresa;
    struct osoba *p_dalsi;
    struct osoba *p_predchadzajuci;
} OSOBA;

sme prečo premennú adresa s dátovým typom ADRESA definovali ako hodnotu a nie ako operátor? ADRESA *p_adresa;

 
Nahoru Odpovědět
16.7.2021 15:22
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Martin Russin
DarkCoder:16.7.2021 18:50

Nejlepší bude si to ukázat na jednoduchém příkladě:

#include <stdio.h>
#include <stdlib.h>

#define MEM_COUNT 10

struct sPoint {
        int x;
        int y;
};

typedef struct sPoint POINT;
// typedef struct sPoint DIMENSION;
// typedef struct sPoint RANGE;

POINT* Alloc(int size);

int main(int argc, char* argv[]) {
        POINT* pVector = NULL;

        pVector = Alloc(MEM_COUNT);
        if (!pVector) {
                puts("Chyba alokace");
                exit(1);
        }

        for (int i = 0; i < MEM_COUNT; i++) {
                pVector->x = i;
                pVector->y = i;
                printf("[%d,%d]\n", pVector->x, pVector->y);
        }

        free(pVector);
        pVector = NULL;

        return 0;
}

POINT* Alloc(int size) {
        POINT* p = (POINT*)malloc(size * sizeof(POINT));
        return p;
}

V programu byla deklarována struktura sPoint, která slouží jako základ pro vytvoření nových typů (POINT, DIMENSION, RANGE), které jsou si podobné, ale mohou mít v programu jiný význam. Deklarace struktury a vytvoření nového typu je oddělené. Lze tak definovat typy později kdekoli v programu při zachování čitelnosti.

Dále v programu je funkce Alloc() která má na starost alokaci paměti a vrátit ukazatel na tuto paměť.
Přiřazení vráceného ukazatele je prosté:

pVector = Alloc(MEM_COUNT);

Funkce vrací NULL nebo ukazatel na začátek alokované paměti. Je tedy třeba provést validitu ukazatele.

Následuje práce s polem - přiřazení hodnot, výpis.

Po skončení práce s polem přichází na řadu uvolnění paměti. Zde je třeba si všimnout pro jaký ukazatel se funkce free() volá.
Poté ukazatel na nic neukazuje a je dobré mu tak přiřadit hodnotu NULL.

Akceptované řešení
+20 Zkušeností
+2,50 Kč
Řešení problému
Nahoru Odpovědět
16.7.2021 18:50
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Martin Russin
DarkCoder:16.7.2021 19:01

Rozdíl mezi ADRESA adresa a ADRESA *adresa spočívá v tom, že v prvním případě je již vyhraněná pamět o velikosti 1 adresového záznamu, kdežto v druhém případě není vyhraněna žádná paměť. Ta se musí vyhranit dynamicky a dále může reprezentovat pole ADRESA, nikoli jen prostor pro jednu ADRESA proměnnou.

Pro práci se strukturami se používají z důvodu efektivity výhradně ukazatele (vrácení ukazatele na strukturu, ukazatel na strukturu jako parametr). Pokud víme, že se bude používat určitý počet struktur, je vhodné použít statickou alokaci. Dynamická alokace vyžaduje určitou režii, což by mohlo vést k méně efektivnímu kódu.

Je třeba vnímat to, jak program bude fungovat, podle toho program psát...

Nahoru Odpovědět
16.7.2021 19:01
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Petan
Člen
Avatar
Odpovídá na DarkCoder
Petan:17.7.2021 20:06

Ahoj jenom drobnost' ADRESA *adresa alokuje pamet velikosti jednoho pointru

 
Nahoru Odpovědět
17.7.2021 20:06
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Petan
DarkCoder:17.7.2021 23:14

Ano, ve druhém případě je mimo rámec ADRESA objektu staticky alokována paměť pro jeden ukazatel na typ ADRESA.

Nahoru Odpovědět
17.7.2021 23:14
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Odpovídá na DarkCoder
Martin Russin:18.7.2021 11:34
typedef struct uzol {
    int hodnota;
    struct uzol *p_dalsi;
} UZOL;

Štruktúru vyššie som správne prepísal na kód, v ktorom som najprv deklaroval štruktúru a potom ju použil na vytvorenie nového dátového typu UZOL?

struct uzol {
        int hodnota;
        struct uzol *p_dalsi;
}

typedef struct uzol UZOL;

.
.
Prečo si použil najprv tento príkaz? Deklaroval si funkciu?

POINT* Alloc(int size);

Na konci programu sa nachádza funkcia, nemohol by som ju uviesť namiesto POINT* Alloc(int size);?

POINT* Alloc(int size) {
        POINT* p = (POINT*)malloc(size * sizeof(POINT));
        return p;
}

.
.

POINT* pVector = NULL;
pVector = NULL;

Obidva riadky majú rovnaký význam? Ukazovateľ neukazuje na žiadnu adresu pamäti? teda na nič neukazuje?
.
.

if (!pVector)

je to iste ako

if (pVector != NULL)

?
.
.
Príkaz puts() má tu istú funkciu ako scanf() ale používa sa iba na vypísanie znakov?
.
.
V Tvojom kóde sme si alokovali pamäť o veľkosti desiatich dátových typov POINT, teda správne to chápem, že pre 10 štruktúr s názvom sPoint dátového typu POINT?

 
Nahoru Odpovědět
18.7.2021 11:34
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Martin Russin
DarkCoder:18.7.2021 13:15

Ano, přepis uvedeného kódu na oddělenou deklaraci struktury a definování nového typu, je správný.

Uvedený příkaz je prototyp funkce (deklarace funkce). Ano, lze jej nahradit definicí funkce, ale není to vhodné. Až se naučíš pracovat s vícero soubory, zjistíš, proč je to důležité.

Výsledek obou příkazů je stejný, přesto jsou tam odlišnosti. První je definice proměnné s inicializací, druhý je příkaz. Oba způsoby nastavují ukazatel na NULL, což značí, že ukazatel na nic neukazuje.

Není to totéž, obráceně.

if (pVector != NULL)
// Je totéž co
if (pVector)

a

if (pVector == NULL)
// Je totéž co
if (!pVector)

Funkce puts() vypisuje na obrazovku řetězec jehož ukazatel je předáván funkci jako její argument. Má blíže funkci printf(), která je obecná. Funkce scanf() slouží pro čtení, nikoli pro zápis.

Ano, funkce Alloc() má na starost alokování deseti objektů typu POINT, což je struktura sPoint.

Nahoru Odpovědět
18.7.2021 13:15
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 15 zpráv z 15.