Práce s ukazateli – dvourozměrné pole



Procvičování práce s pamětí a vícerozměrnými proměnnými.



Zadání

Napište program, který bude realizovat základní práci s dvourozměrným (2D) dynamickým polem (=oba dva rozměry pole lze v během programu měnit => základem pole je TYP ** (např. double**)).

Implementujte funkce:



Pro pokročilé:



Těla funcí doplňte do vzoru uvedeného na konci tohoto souboru. Řádky ve funkci main pouze upravujte, nemažte. Hlavičkový soubor vytvořte sami.

Korektnost manipulace s pamětí v programu ověřte přidáním modulů knihovny check, který zajistí tisk informací o naalokované paměti a tedy informaci o správné činnosti programu. Popis přidání modulu do programu a funkce pro volání jsou popsány v souboru check.h.



Postup práce:

  1. Vytvořte nový prázdný projekt.

  2. Vytvořte soubor main2D.c, funkce2d.c, funkce2d.h a vložte je do projektu.

  3. Do souborů vložte odpovídající část připraveného kódu z konce této stránky.

  4. Zkuste přeložit a upravte případné chyby (provádějte pouze opravy chyb, nevytvářejte nový program). V rámci tohoto bodu můžete zkusit napsat chybnou alokaci/odalokaci a vyzkoušet funkci knihovny check.
    - Překlad začněte tak, že se přepnete do souboru funkce2D.c a podíváte se, kde jsou hlášeny chyby. Překlad pouze tohoto modulu je možný pomocí volby BUILD/COMPILE (CTRL+F7).
    - Do souboru funkce2D.c přidáte include souboru funkce2d.h, který je zatím prázdný.
    - Na základě popisu (zdrojový kód níže) co patří do hlavičkového souboru, v něm vytvořte typ MUJTYP, a enum pro kódy chyb. Hlavičkový soubor ošetřete proti vícenásobnému načtení. Zároveň si připravte i formátovací řetězec pro tisk, který bude příslušný k typu nadefinovanému v MUJTYP. Nyní by měl jít modul funkce2D.c bez problémů zkompilovat → měl by být správný z hlediska jazyka C (ne však funkce, protože těla funkcí jsou prázdná).
    - Nyní přeložte celý projekt (nebo zkompilujte main2d.c). Zjistíme, že opět musíme naincludovat hlavičkový soubor funkce2D.h, ve kterém jsou používané definice.
    - Jelikož máme přípony souborů .c, překládá se podle jazyka C. Výsledkem tedy je, že překlad proběhne, i když překladač „nezná“ prototypy funkcí. Pouze nahlásí warningy. Toto řešení je ovšem špatné a tak musíme warningy odstranit. To uděláme tím, že hlavičky funkcí ze souboru funkce2D.c překopírujeme do souboru funkce2d.h a tím oznámíme prototypy funkcí, které jsou v tomto souboru. Do hlavičkového souboru nedáváme prototypy funkcí static – ty jsou lokální pro daný soubor – jsou to funkce pomocné, které nejsou určené k všeobecnému použití.

  5. Všude, kde se vyskytuje typ uložený v poli, používejte zástupný typ MUJTYP (pomocí #define). Tam kde je stejný typ, ale nemá spojitost s prvky pole používejte standardní název typu.

  6. Postupně začněte vypracovávat těla metod. Motivační obrázek pro „tvar“ 2D pole v paměti a návaznosti je uveden níže.
    Uvažujte jaké chyby mohou nastat. Každá metoda by měla mít tři (hlavní) části:
    - test zda parametry jsou v pořádku, že obsahují data, jsou vhodná pro výpočet a že výpočet je s nimi možné provést
    - vlastní výpočet
    - test kvality výpočtu/výsledku - pokud výsledek nelze určit, není správný nebo je "nekvalitní", nastavte výstup, nebo výstupní proměnnou tak, aby ostatní funkce věděly, že tato proměnná není vhodná k následujícím výpočtům.

  7. Seznamte se s knihovnou check a připojte ji k projektu. Doplňtě ji (pomocí posledního inlcude v modulu) do všech zdrojových souborů (*.c).

  8. Navrhněte testovací volání funcí (některá volání jsou již uvedena; nezapomeňte okomentovat proč tam jsou, jaký stav mají za úkol testovat).





Motivační nákres situace po naalokování 2D pole

)



============= vzor pro zkopírování - začátek ==================

============= funkce2d.h ==============

Standardní ošetření hlavičky.

Nadefinování typu pole pomocí define nebo typedef:
#define MUJTYP double


Nadefinování řetězce pro tisk definované proměnné příslušný k MUJTYP:
#define MUJTYP_PRINTF “%.2f“

Pro vracené chyby vracejte kódy pomocí typu enum:
enum TMatrixError { EOk = 0, EWrongSize, ENoMemory, ENotAllocated, EAlreadyAllocated};

Prototypy funkcí využitých v c



============= funkce2d.c ==============



static MUJTYP** allocate(unsigned aX, unsigned aY)

{

MUJTYP **mat;

// Alokuje dvourozmerne dynamicke pole s rozměry x a y a prvky typu MUJTYP

// if() return(NULL); // nepodaril se naalokovat vektor ukazatelů

// if() { return(NULL);} // nepodarilo se naalokovat nektery z vektoru hodnot (radek)


return(mat); // naalokovana matice

}


static void deallocate(MUJTYP **aMat, unsigned aY)

{

// vlastni odalokovani 2D pole s nastavenim hodnoty pole tak,

// aby bylo jasne, ze je odalokovano


}


static void fill(MUJTYP **aMat, unsigned aX, unsigned aY, MUJTYP aValue)

{

// naplnění matice hodnotou

}


enum TMatrixError allocate_matrix(MUJTYP ***aMat, unsigned aX, unsigned aY, MUJTYP aValue)

{

// Dyn. alokace dvourozmern. pole velikosti x,y

// s prvky nastavenymi na hodnotu value

// vraci: 1 pokud je jiz naalokovano, jinak 0 (OK),

// pokud pole existuje, pak se nealokuje

// alokace se provede pomocí volání funkce Alokuj

// pokud dojde k chybě při alokaci, vrací se kód 2

// následně se provede naplneni hodnotou pomocí funkce fill_value


// if() return(EAlreadyAllocated); // jiz naalokovano


//if()return(EWrongSize); // nektery ze zadanych rozmeru je spatny (nula)

//if()return(ENoMemory); // funkci Allocate se nepodarilo naalokovat pamet


return(EOk); // Vse je v poradku

}


enum TMatrixError print_matrix(MUJTYP** aMat, unsigned aX, unsigned aY)

{

// Vytisknuti pole mat o velikosti x,y

// if() {return(EOk);} // matice je prazdna pouze tisk oznameni

//if() return(EWrongSize); // nektery ze zadanych rozmeru je spatny (nula)


return(EOk);

}


enum TMatrixError add_matrix(MUJTYP** aMat_a, unsigned aX_a, unsigned aY_a, MUJTYP** aMat_b, unsigned aX_b, unsigned aY_b, MUJTYP ***aMat_c, unsigned *aX_c, unsigned *aY_c)

{

// soucet matic (po prvcich) mat_c=mat_a + mat_b o velikosti x,y

// kontrola zda jsou mat_a, mat_b a mat_c stejnych rozmeru – chyba kód 1

// kontrola „smysluplnosti“ parametrů (mat_a a mat_b musí být naalokovány) – chyba kód 2

// pokud mat_c neexistuje pak ho naalokovat

// pokud mat_c má jiné rozměry, pak ho přealokovat na správný rozměr

// provest součet


// if()return(ENotAllocated); // nektera z matic je prazdna (nenaalokovana)

// if( ) return(EWrongSize); // nektery ze zadanych rozmeru je spatny (nula) – staci u jedne matice + další radky

// if () return(EWrongSize); // matice nemaji stejny rozmer y

// if () return(EWrongSize); // matice nemaji stejny rozmer x


return(EOk);

}


enum TMatrixError deallocate_matrix(MUJTYP*** aMat, unsigned aX, unsigned aY)

{

// Dealokace dvourozmer. pole o velikosti x,y

// nastavit mat tak, aby bylo zrejme, ze je odalokovano

// provede se pomocí volání funkce Odalokuj


return(EOk);

}


// ======================= pro pokročilé ===========================

/* tuto funkci muzete odladit az v ramci implementace struktur

v pripade, ze ji budete odladovat bez structur, je potrebne upravit parametry

funkcí volanych pro transponovane promenne ve funkci main (nevyhoda reseni dat a rozmeru samostatne)

*/

enum TMatrixError transpose_matrix(MUJTYP ***aMat, unsigned *aX, unsigned *aY)

{

// transpozice matice

// naalokovani pomocne transponované matice - Alokuj

// prekopirovani hodnot do nove matice

// odalokovani stare matice - disp_matrix

// presmerovani nove matice do stare (pomoci prirazeni ukazatele)


return(EOk);

}

============== main2d.c =========================



#include <stdio.h>

#include <stdlib.h>



// hodnoty rozmeru pole pro uvodni ladeni

// pro dalsi ladeni zkuste matice ruznych rozmeru


#define X 5

#define Y 6


int main(int argv, char *argc[])

{

MUJTYP **amat = NULL;

MUJTYP **bmat = NULL;

MUJTYP **cmat = NULL;


printf("\nAlokuju.........\n");

if(allocate_matrix(&amat, X, Y, 2) != EOk)

{ fprintf(stderr, "Nedostatek pameti.\n"); return 1; }

// nesmim naalokovat dvakrat

if(allocate_matrix(&amat, X, Y, 2) != EAlreadyAllocated)

{ fprintf(stderr, "Nefunguje test na opakovanou alokaci.\n"); return 1; }


if(allocate_matrix(&bmat, X, Y, 3) != EOk)

{ fprintf(stderr, "Nedostatek pameti.\n"); return 1; }

if(allocate_matrix(&cmat, X, Y, 0) != EOk)

{ fprintf(stderr, "Nedostatek pameti.\n"); return 1; }


amat[0][0] = 4; // Pristup do pole

unsigned int cX = X, cY = Y;

print_matrix(amat, X, Y);

print_matrix(bmat, X, Y);

add_matrix(amat, X, Y, bmat, X, Y, &cmat, &cX, &cY);

print_matrix(cmat, cX, cY);

transpose_matrix(&cmat, &cX, &cY);

// nasobeni_matrix(&amat,&nX,&nY, bmat,X,Y, cmat,Y,X);

print_matrix(cmat, cX, cY);

//memory_stat();

printf("\nDealokuju.........\n");

deallocate_matrix(&amat, X, Y);

deallocate_matrix(&bmat, X, Y);

deallocate_matrix(&cmat, cX, cY);

deallocate_matrix(&cmat, cX, cY); // nesmim odalokovat dvakrat

// nesmim pracovat s prazdnymi (odalokovanymi)

print_matrix(amat, X, Y);

print_matrix(bmat, X, Y);

add_matrix(amat, X, Y, bmat, X, Y, &cmat, &cX, &cY);

print_matrix(cmat, cX, cY);

//transpose_matrix(&cmat, &cX, &cY);

print_matrix(cmat, cX, cY);

// musim byt schopen opetovne pouzit prazdnou promennou

if(allocate_matrix(&amat, X, Y, 2) != EOk) { printf("Nedostatek pameti.\n"); return; }

print_matrix(amat, X, Y);

deallocate_matrix(&amat, X, Y);

printf("\nKonec programu.........\n");


return 0;

}

=========== konec vzoru ===============













Poslední změna 2014-10-09