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í).
Naalokování dvourozměrného pole (matice) s inicializací všech prvků na danou hodnotu (můžete zkusit více způsobů vracení - jako parametr, nebo návratová hodnota ...)
odalokování dvourozměrného pole (matice)
vstup ze stremu (FILE*) a výstup do streamu (FILE*) pro dvourozměrné pole (matici)
další možnosti bude-li čas: násobení matic, transpozice pole/matice (alokace nového pole do pomocné proměnné, naplnění hodnotami, odalokace starého pole, uložení nového pole); a=b*bT. Součet dvou matic do třetí (alokace nového pole, sečtou se odpovídající si prvky matic (cij = aij + bij))
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:
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.
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.
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á.
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.
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.
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).
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..
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.
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.
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.
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í.
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