Práce s ukazateli - dvourozměrné pole



Zadání

Napište základní funkce pro práci s dvourozměrnými poli/maticemi.


Pole realizujte jako dynamické. Dynamickým polem rozumíme pole, které je definováno pomocí ukazatelů (pro dvourozměrné pole tedy TYP **). U dynamického pole máme možnost měnit jeho velikost za chodu programu. Pole je realizováno pomocí ukazatelů a (fyzicky) je vytvořené alokací paměti až za chodu programu (na rozdíl od standardního pole (lokální porměnná ve funkci), které má dán pevný rozměr při překladu)). Dynamické pole je nutné po použití odalokovat (vrátit použitou paměť systému).
Funkce by měly na začátku obsahovat testy (jsou rozměry smysluplné? (tj. větší než nula). Jsou matice inicializovány?). Na konci by funkce (které neprovedou co měly, nebo provádí odalokaci) měly matice nastavit do stavu aby se s nimi dále nepracovalo = signalizovat nepoužitelnost matice.
Pole realizujte pomocí struktury s názvem SPole. Jako datový prvek matice zvolte typ double, ale realizujte ho pomocí #define TYP double. Název TYP používejte u všech proměnných spojených s proměnnými uloženými v poli. Tato konstrukce umožní v případě potřeby změnu datového typu uloženého v poli.

Postupně realizujte následující činnosti (napsat funkci, vyzkoušet, po odladění pokračovat další funkcí).



Výsledná struktura programu:

// vytvoření proměnných – inicializace na „prázdné“
// volání funkce tisk – nic se netiskne
// volání funkce pro násobení – nic se nestane
// volání funkce pro odalokování – nic se nestane
// volání funkce pro alokaci
// naplnění daty – libovolné, ze souboru
// volání funkce tisk
// volání funkce pro násobení
// volání funkce tisk
// volání funkce pro odalokaci
// volání funkce tisk – nic se netiskne



Poznámky ke tvorbě programu:

  1. Vytvořte projekt, do kterého vložte tři soubory. V prvním soubloru mainpole.c bude funkce main, ve druhém souboru pole.c budou realizace požadovaných funkcí pro manipulaci s polem. Ve třetím souboru pole.h budou prototypy funkcí. Hlavičkový soubor ošetřete proti vícenásobnému načtení a vložte ho do zdrojových souborů.
    Do projektu vložte funkce pro kontrolu paměti check.c a check.h (check.h načtěte do všech zdrojových souborů jako poslední načítání).
    Funkce opět vytvářejte postupně – vyzkoušejte v main jako kód bez volání, následně jako funkci ve stejném souboru jako main a nakonec funkci přesuňte do jejího zdrojového souboru.

  2. Nadefinujte proměnnnou TYP. Vytvořte strukturu SPole pro uložení pole. Bude obsahovat proměnné Radky a Sloupce pro uchování rozměrů pole (size_t). Dále bude obsahovat ukazatel Mat na dvourozměrné dynamické pole prvků typu TYP. Definice typu i struktury netvoří kód a bude využívána ve více modulech – proto obě umístíme do hlavičkového souboru.

  3. Ve funkci main vytvořte proměnnou pa typu struct SPole pro uložení dvourozměrného pole. Proměnnou inicializujte v definici tak aby byla prázdná.

  4. Napište funkci Alokuj, která naalokuje dvourozměrné pole. Bude mít dva vstupní parametry obsahující rozměr alokovaného pole. Návratovou hodnotou bude ukazatel na vytvořené pole. V případě neúspěchu alokace, nebo bude-li některý z rozměrů nula, vrátí hodnotu NULL. Funkce bude umístěna v souboru pole.c a v souboru pole.h bude její prototyp.
    TYP ** Alokuj(size_t aSloupcu, size_t aRadku)
    Pozn.: rozlišujte mezi hodnotou 0 (nula) a NULL. Pro číselné typy se používá 0 (nula), pro ukazatele NULL.

  5. Ve funkci main si vytvořte pomocnou proměnnou TYP ** pom. Zavolejte funkci Alokuj s hodnotami nula na místě řádků a sloupců (všechny kombinace) a výsledek uložte do proměnné pom. V případě, že funkce nevrátí hodnotu NULL, vytiskněte na standardní chybový výstup „chyba ve funkci Alokuj“.
    Pomocí funkce Alokuj naalokujte pole o rozměrech 3 řádky a 4 sloupce. Vrácený ukazatel uložte do proměnné pa. Ve stejné proměnné nastavte i rozměry uloženého pole.
    Přeložte, spusťte a podívejte se co píše check. Mělo by dojít ke čtyřem alokacím, které nejsou odalokovány.

  6. Ve funkci main naplňte prvky pole v proměnné pa hodnotami rovnajícími se součtu indexu řádku a indexu sloupce na dané pozici (indexy od nuly).

  7. Napište funkci Tisk, která vytiskne obsah pole. Funkce bude mít jeden parametry typu struct SPole. Pole vytiskne ve formátu:
    [Pocet_Radku][Pocet_Sloupcu]
    hodnoty prvniho radku (oddělené středníkem a mezerou)
    hodnoty druheho radku (oddělené středníkem a mezerou) …

    void Tisk(struct SPole aPole, FILE *aVystup);

    V případě, že aVystup bude NULL nic netiskněte.
    Funkci zavolejte pro proměnnou pa před alokací paměti v proměnné i po alokaci a naplnění hodnotami. Ověřte, že funkce pracuje pro inicializovanou i neinicializovanou proměnnou..

  8. Napište funkci Odalokuj pro odalokování pole. Odalokujte nejprve všechny řádky a potom pole pro ukazatele na řádky. Po uvolnění paměti vynulujte všechny proměnné struktury.

    void Odalokuj(struct SPole *aPole); Proč zde, na rozdíl od funkce Tisk, musí být ukazatel?
    Funkci zavolejte pro proměnnou pa po tisku. Následně zavolejte funkci Odalokuj ještě jednou – již by neměla nic udělat.
    Přeložte, spusťte a podívejte se co píše check. Nemělo by dojít k hlášení žádné chyby, paměť by měla být všechna vrácena.

  9. Otevřete pro zápis textový soubor „vystup.txt“ a uložte do něj pole pa (v místě, kde je naplněno hodnotami) pomocí funkce Tisk. Soubor následně zavřete. Správnost zkontrolujte.

  10. Napište funkci Nacti, která naplní proměnnou typu struct SPole ze souboru. Funkce bude mít dva parametry – strukturu pro uložení dat a soubor pro čtení.
    int Nacti(struct SPole *aPole, FILE* aVstup);
    V případě úspěchu vrací hodnotu 0. V případě chyby hodnotu 1. Formát dat v souboru je stejný jako u funkce Tisk.

    Funkce ze souboru načte rozměry (vyčte také závorky okolo nich). Následně alokuje pomocí funkce Alokuj paměť, kterou společně s rozměry zapíše do struktury. Hodnoty v poli naplní ze souboru.
    Vytvořte v main proměnnou pb. Tuto proměnnou inicializujte v definici na nulové hodnoty. Otevřete soubor vystup.txt“ pro čtení a naplňte z něj proměnnou pb pomocí volání funkce Nacti.

    Proměnnou pb vytiskněte. Nezapomeňte ji odalokovat a zavřít soubor.

  11. Napište funkci Nasob, která provede součin dvou matic.
    Vytvořte si dva soubory se vstupními daty dvou polí. Vytvořte tři proměnné typu struct SPole a dvě z nich naplňte hodnotami ze souborů. Zavolejte funkci Nasob a vytiskněte výsledek.
    int Nasob(struct SPole aM1, struct SPole aM2, struct SPole *aVysledek);
    Hodnota prvku při násobení je a(i,j) = Suma přes k: aM1(i,k) * aM2(k,j)

    Zkontrolujte výsledné pole po násobení, hospodaření s pamětí a soubory (check).
    Funkce by měla otestovat, zda jsou matice „použitelné“ - tj. existují a mají správné rozměry pro násobení.

  12. Napište funkci VytvorPole(struct SPole *aPole, size_t aRadku, size_t aSloupcu, TYP aInitHodnota), která vytvoří ve struktuře dané pole a vyplní ho danou hodnotou.


Motivační ukázka realizace 2D pole v paměti







Poslední změna 2019-02-01