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í.

Diskuze: Domácí úkol FIT ČVUT (progtest 5.1/2019)

V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.

Aktivity
Avatar
David Gerner
Člen
Avatar
David Gerner:29.11.2019 19:16

Ahoj, máme zadaný domácí úkol na hledání min, program dostane po řádcích pole s minami a má vytvořit vyřešené pole s minami, tedy to na konci, když se vám všude zobrazí správná čísla s počty min v okolí. Zadání přiložím.
Zdůrazňuji že rozhodně nechci aby to někdo vypracoval za mě, potřebuju spíš nakopnout do zadku(hlavy) protože jsem úplně ztracen a nevím vůbec co s tím.

Díky za jakékoli nápady či připomínky ;)

Zkusil jsem: Tady je nějaký můj pokus, rozhodně to není funkční program, spíš jen takové myšlenkové črty, ale jsou všechny špatně, tak spíš abyste věděli jakým směrem se ubíraly mé dosavadní úvahy.

void fillArray(char ** array, int n, int m);
char ** solveMines (char ** array);

int main (void){
        int n = 100, m = 100;
        char ** mineField = (char **) malloc (sizeof(char)*n);
        for(int i=0; i<100; i++)
                mineField[i] = (char *) malloc(sizeof(char)*m);
        printf("Zadejte hraci plochu:\n")
        fillArray(mineField, n, m);

}

void fillArray(char ** array, int n){   //takhle funkce má vyplnit pole těmi zadanými hodnotami, ale nevím jak na to protože nevím předem jak jsou řádky dlouhé ani kolik jich bude
        char buffer[101];
        for (int i=0; i<n; i++){
                scanf("%100[^\n]", buffer);
                strcpy(array, buffer);
        }
}

char ** solveMines (char ** array){ //tady jsem zas chtěl nějak porovnávat sousední pole, ale nemyslím že to bude moc fungovat, navíc nevím jak pak jít o řádek níž
        int i=0, j=0;
        while(array[i][j]){
                while (array[i][j]){
                        if(array[i][j]=='.'){
                                if(array[i+1][j]=='*'&&array[i][j+1]!='*'&&array[i+1][j+1]!='*')
                                        array[i][j]='1';
                                else if(array[i][j+1]=='*'&&array[i+1][j+1]!='*'&&array[i+1][j]!='*')
                                        array[i][j]='1';
                                else if(array[i+1][j+1]=='*'&&array[i+1][j]!='*'&&array[i][j+1]!='*')
                                        array[i][j]='1';
                                else if(array[i+1][j]=='*'&&array[i][j+1]=='*'&&array[i+1][j+1]!='*')
                                        array[i][j]='2';
                                else if(array[i+1][j]=='*'&&array[i][j+1]!='*'&&array[i+1][j+1]=='*')
                                        array[i][j]='2';
                                else if(array[i+1][j]!='*'&&array[i][j+1]=='*'&&array[i+1][j+1]=='*')
                                        array[i][j]='2';
                                else if (array[i+1][j]=='*'&&array[i][j+1]=='*'&&array[i+1][j+1]=='*')
                                        array[i][j]='3';
                        }
                        i++;
                }
                j++
        }
}

Tohle je komplet všechno co mám, bohužel.

 
Odpovědět
29.11.2019 19:16
Avatar
Matěj
Člen
Avatar
Matěj:29.11.2019 19:42

Tu lavinu podmínek bych nahradil jedním for cyklem, který by v případě, že se jedná o minu, přidal všem pozicím bez miny okolo, tedy začneš v levém horním rohu 3x3 čtverce vůči té pozici s minou a kontroluješ jestli je to validní pozice, +1.

Editováno 29.11.2019 19:44
 
Nahoru Odpovědět
29.11.2019 19:42
Avatar
David Gerner
Člen
Avatar
Odpovídá na Matěj
David Gerner:29.11.2019 19:46

Aaa, díky, to mě vúůbec nenapadlo jít po minách ;)
akorát ono je to žejo pole charů a všude kolem je tečka, takže bych musel mít nějaký tmp proměnný abych tam mohl ukládat ty čísla a až bych zkontroloval celé okolí bodu tak bych to teprv mohl zapsat, ne...?

 
Nahoru Odpovědět
29.11.2019 19:46
Avatar
Matěj
Člen
Avatar
Matěj:29.11.2019 19:50

Nemusíš jít čistě jen po minách, bylo by to sice efektivnější, ale musel by sis pamatovat v dalším poli jejich pozice, stačí do tech 2 while cyklu který ti procházej celý pole přidat if: pokud se jedná o minu zavolej nad tohle pozici funkci, která děla to co jsem popsal výše. Protože jsou to všechno znaky, tak tečku budeš brát jako 0 a pokud tam bude něco jiného než tečka a hvězdička, přetypuješ to na číslo, přičteš jedničku a uložíš zpátky jako char.

Editováno 29.11.2019 19:51
 
Nahoru Odpovědět
29.11.2019 19:50
Avatar
Matěj
Člen
Avatar
Matěj:29.11.2019 20:02

A ohledně toho načítání, když předem nevíš kolik toho bude, tak ve while cyklu budeš načítat po znacích např přes getchar() a v případě, že ti dojde místo v poli, zdvojnásobíš kapacitu a zavoláš realloc().

 
Nahoru Odpovědět
29.11.2019 20:02
Avatar
DarkCoder
Člen
Avatar
Odpovídá na David Gerner
DarkCoder:30.11.2019 13:42

program dostane po řádcích pole s minami…

Toto je dosti neúplná informace. Je třeba blíže specifikovat z jakého vstupu ta data program obdrží popř. jaký je ukázkový vstup, neboť způsob komunikace mezi vstupem a programem se značně liší. Jsem si téměř jist, že ta data budou posílána ze souboru a to z následujícího důvodu:

Vždy potřebuješ znát ukončovací signál

V předchozím příspěvku aXxel správně zmínil, jakým způsobem se přistupuje ke vstupu, jehož velikost není známa. getchar() pro konzolu, lépe však pomocí fgetc() s konkrétním streamem. Pro manipulaci s načteným znakem pak můžeš používat dynamickou alokaci paměti nebo souborový systém.

Pokud je vstupem soubor, je situace jednoduchá. Souborový systém posílá informaci, zda-li došlo k chybě nebo na konec souboru (EOF). Tuto informaci lze otestovat. To je signál k tomu, že už na vstupu nic dalšího není (platí v případě, že EOF představuje konec souboru nikoli chybu).

Úryvek kódu pro načítání ze souboru neznámého počtu dat bez ošetření na chybu

do {
        c = fgetc(fp);
        if (feof(fp)) break;
        putchar(c);
} while (1);

Avšak pokud je vstupem klávesnice, je situace naopak neřešitelná. Když Ti začnu na klávesnici postupně házet řádky dat:

Např.
001000100010'\n'
100011000011'\n'
110110011100'\n'
….

Tak nevíš, jestli poslední zadaný řádek ukončený znakem '\n' byl skutečně tím posledním nebo bude další řádek následovat. Při načítání dat Ti celý načítací cyklus zůstane v blokovacím režimu na čtecí funkci očekávající nový řádek. Ten já mohu poslat ale nemusím. Signál (EOF), který byl u načítání dat ze souboru, se zde neposílá. Nelze použít signál v podobě znaku '\n'´pro ukončení cyklu, neboť tento znak je součástí procedury načítání řádku.

Následující úryvek kódu nebude pracovat správně pro načtení vstupu po řádcích

do {
        c = getchar();
        if (c == '\n') break;
        putchar(c);
} while (1);

Proto nejlepší bude, pokud sem pošleš přesné znění zadání. (Pozn. Není za účelem zpracování úlohy za tebe)

Akceptované řešení
+20 Zkušeností
+2,50 Kč
Řešení problému
Nahoru Odpovědět
30.11.2019 13:42
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Odpovídá na DarkCoder
Patrik Valkovič:30.11.2019 14:51

To není úplně pravda, i z konzole lze vyvolat EOF. Data jdou ze standardního vstupu.

Nahoru Odpovědět
30.11.2019 14:51
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Patrik Valkovič
DarkCoder:30.11.2019 15:32

V případě příkladu vstupu, který jsem uvedl (varianta čtení nespecifikovaného počtu řádků z klávesnice), se EOF neposílá. To ale neznamená, že EOF nelze explicitně vyvolat i ze standardního vstupu. To ano, to máš pravdu. Kombinace CTRL+D popř. CTRL-Z (dle OS) tento signál vyvolá. Ukončovat vstup tímto způsobem je absolutně zcestné. Na to slouží příkazově orientované rozhraní v podobě klíčového slova.

Nahoru Odpovědět
30.11.2019 15:32
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
David Gerner
Člen
Avatar
Odpovídá na DarkCoder
David Gerner:6.12.2019 10:44

Celé zadání je zasláno ve screenshotech. Z toho co jsem tam přečetl jsem pochopil že bude zadáváno po řádcích a že na konec stejně bude EoF. Ale jistej si tím nejsem.
Každopádně vzhledem k tomu že už je po odevzdávacím datu, tak klidně nic nedělej, já to někomu fajfknu a já si s tím budu hrát až někdy až se budu nudit ;)

 
Nahoru Odpovědět
6.12.2019 10:44
Avatar
Odpovídá na David Gerner
Erik Šťastný:6.12.2019 12:24

Můžu prosím ze zvědavosti o jaký semestr FITu, jde? :)

 
Nahoru Odpovědět
6.12.2019 12:24
Avatar
Lu Kiss
Člen
Avatar
Odpovídá na Erik Šťastný
Lu Kiss:7.12.2019 10:54

Co jsem tak slyšel, tak slavný Vagnerův Progtest na C je v prvnim semestru

 
Nahoru Odpovědět
7.12.2019 10:54
Avatar
Odpovídá na Lu Kiss
Erik Šťastný:9.12.2019 10:52

Díky za info, vzhledem k tomu, že se jde na VŠ od nuly, tak mi přišlo, že pro první semestr je to celkem HC pro člověka co se před pár měsíci dozvěděl co je to programování :D

 
Nahoru Odpovědět
9.12.2019 10:52
Avatar
Honza Bittner
Tvůrce
Avatar
Odpovídá na Erik Šťastný
Honza Bittner:9.12.2019 13:38

A tos neviděl úkoly na další semestry. :-)))

Nahoru Odpovědět
9.12.2019 13:38
FIT ČVUT alumnus :-) Sleduj mě na https://twitter.com/tenhobi a ptej se na cokoli na https://github.com/tenhobi/ama.
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Erik Šťastný
DarkCoder:9.12.2019 14:04

Teď Ti to možná připadá těžké, protože nevíš co a jak a kdy správně použít. Jakmile se ale dozvíš o jazyku C více, bude Ti to připadat úsměvné a snadné. Postupně budou tvé aplikace elegantnější a efektivnější.

Nahoru Odpovědět
9.12.2019 14:04
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Odpovídá na DarkCoder
Erik Šťastný:9.12.2019 14:05

Mě to těžké nepřijde, přijde mi to těžké na někoho, kdo se učí programovat 4 měsíce :)

 
Nahoru Odpovědět
9.12.2019 14:05
Avatar
Ladislav Vagner:9.12.2019 14:16

Dobrý den,
Myslím si že toto asi nebude nejlepší řešení Vašeho problému.
Stavte se na konzultaci po prosemináři.

 
Nahoru Odpovědět
9.12.2019 14:16
Avatar
David Gerner
Člen
Avatar
 
Nahoru Odpovědět
11.12.2019 13:34
Avatar
JerryM
Člen
Avatar
Odpovídá na David Gerner
JerryM:28.12.2019 10:13

čau kluku to máš docela jednoduchý. jestli teda zadáš jen to hrací pole kde sou miny a pak jako výstup spočítáš ty celočíselné hodnoty sousedství kolik je kde min. Děláš to v klasickým C jazyku, požadavek na plnění normy ISO ani K&R nemáš takže pohoda.

Dělá se to tak, že si uděláš záznam struct a do něj dáš hodoty

struct Cell {
   boolean isMineHere;
   byte x;
   byte y;
   byte counterSurr;
} cell;

a pak vytvoříš dvoudimenzionální pole záznamů Cell

Cell playingArea[10][10];

viz zde

https://stackoverflow.com/…-struct-in-c

for (int i=0; i < 10; i++){
   for (int j=0; j < 10; j++){
      playingArea[i][j] = malloc(sizeof(Cell));
   }
}

no a to máš skoro celý hotový .... teď máš hrací pole o velikosti 10x10 min jako klasický pole
a máš přidělenou paměť takže teď už rovnou vyplníš hodnoty isMineHere jestli tam je mina nebo podle ručního zadání z klávesnice a podle toho nastavíš hodnotu isMineHere a pak spočítáš hodnoty pole counterSurr pro každou buňku, který udává kolik min je v sousedství každé buňky

for (int x=0; x < 10; x++){
   for (int y=0; y < 10; y++){

       tady uděláš 8x if za sebou a v každým se ptáš jestli v okolí dané pozice x,y
       je nějaká mina a výsledky sečteš takže jako příklad ptáš se na pozici x=0,y=0
       a  sčítáš miny v okolí a zároveň kontroluješ jestli nejsi mimo hrací pole, počátek
       je vlevo  nahoře

      mineCounter = 0

      if x-1 >= 0 && y-1>=0 { If playingArea[x-1][y-1].isMineHere == true {mineCounter++} }
      if x-1 >= 0 && y+0>=0 { If playingArea[x-1][y+0].isMineHere == true {mineCounter++} }
      ... atd musíš všech 8 buněk v osmiokolí... zatim sem ti napsal dvě
   }
}

no a to je všechno no ...
pak ten výsledek zase vypíšeš

for (int y=0; y < 10; y++){      vypisuješ pořádcích a za každým řádkem odentruješ !!!!
   for (int x=0; x < 10; x++){
        printf ...
   }
        printf  tady se dá jenom enter resp vypíšeš ASCII znaky CR+LF na odřádkování
}

takže jak vidíš tak zas tak hrozný to neni... ale pochybuju že tohle budeš schopnej řešit v 1. semestru na vš bez cizí pomoci za pouhých 13 týdnů výuky bez předchozí znalosti programování a při zátěži ostatních předmětů ... podle mě je to přemrštěný ....

 
Nahoru Odpovědět
28.12.2019 10:13
Avatar
DarkCoder
Člen
Avatar
Odpovídá na JerryM
DarkCoder:28.12.2019 16:42

To mu teda radíš pěkné věci. :-)

Už z důvodu, že v čase kompilace se neví, kolik bude posláno dat, tak nelze řešit alokaci pole staticky ale dynamicky. Takže stanovit velikost 2D pole na 10x10 nebo pracovat pouze s touto velikostí, nelze.

struct Cell {
   boolean isMineHere;
   byte x;
   byte y;
   byte counterSurr;
} cell;

C nezná žádné boolean ani byte, pokud si je sám pomoci typedef nenadefinuji. Chceš-li použít Boolovskou proměnnou už definovanou, pak překladač musí splňovat normu C99 a vložit hlavičkový soubor stdbool.h. Typ této proměnné je pak _Bool.

Ve výše uvedené deklaraci je proměnná (cell, která je typu struct Cell), která tam být vůbec nemá. Dále záznamy uvnitř struktury jsou naprosto zbytečné, x a y je dáno indexy 2D pole a není je třeba vůbec zahrnovat do struktury. To zda se na pozici nachází mina nebo jaké tam je číslo říkající o počtu sousedících min lze zapsat do jednoho prvku. Veškerá potřebná data lze tak zapsat do proměnné o velikosti 1 bytu.

struct Cell {
        char info;
}

prvek info může obsahovat následující hodnoty:

hodnota -1 říká, že na políčku je mina
hodnoty 0 až 8 říkají, že na políčku mina není a s kolika minovými políčky sousedí

jak je vidět, položka info obsahuje 10 variant hodnot a ty se mohou vejít do datového typu char.

Pozn: Kdo chce optimalizovat, může použít bitové pole. (Vše se vejde do 4 bitů)

Pokud máme deklarovaný typ struktury tak jej v C nelze volat takto:

Cell playingArea[10][10];

ale je třeba použít klíčové slovo struct. (V C++ už klíčové slovo struct není potřeba)

struct Cell playingArea[10][10];

nebo si z deklarace struktury vytvořit nový typ pomocí typedef.

struct Cell {
        char info;
}

typedef struct Cell CELL;

// a pak proměnná může vypadat např. takto

CELL pole2D[100][100];

nebo

typedef struct {
        char info;
} CELL;

// a pak proměnná může vypadat např. takto

CELL pole2D[100][100];

Dále:

for (int i=0; i < 10; i++){
   for (int j=0; j < 10; j++){
      playingArea[i][j] = malloc(sizeof(Cell));
   }
}

je chybně. Funkce malloc() vrací obecný ukazatel, který je dobré přetypovat na daný typ (v C přetypování není nezbytné ale je to vhodné, v C++ je přetypování u funkce malloc() bezpodmínečné). Levá strana je typu 2D pole nikoli ukazatel na 2D pole.

K úloze:

Jinak jak už zadání napovídá, lze úlohu řešit pomocí realloc(). Ostatně je to úloha na procvičení práce s dynamickou alokací a bylo by vhodné to tímto způsobem řešit. Lze to ovšem řešit i jinak a vyhnout se neustálému testování na to zda právě alokované pole je stále dostačující a realokaci. Data ze standartního vstupu si lze posílat rovnou do souboru. Z velikosti souboru a prvního znaku nového řádku lze určit dimenze 2D pole, na které už lze přímo volat malloc() a vytvořit si tak 2D pole přesných rozměrů. Toto 2D pole naplnit daty ze souboru, provést validaci dat a následně určit vše potřebné a obsah pole vypsat.

Nahoru Odpovědět
28.12.2019 16:42
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
JerryM
Člen
Avatar
Odpovídá na DarkCoder
JerryM:28.12.2019 17:40

ježiš ty seš ale puntičkář ....
tak by si místo Bool dal Char .... to je to samý ...
a pracovat s obecným ukazatelem neni problem.
přetypuje to až bude potřeba
více proměnných ve struktuře taky nevadí
a to pole si definuje s nějakým max. rozměrem,
kterej se pak už nesmí překročit no ...
je to školní úloha.... přece nebude používat realoc ... .
tak by to vypadalo nějak takhle no

// Mines.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>

struct Cell {
        int isMineHere;
        char x;
        char y;
        int counterSurr;
} cell;

int main()
{

        Cell* playingArea[10][10];

        for (int x = 0; x < 10; x++) {
                for (int y = 0; y < 10; y++) {
                        playingArea[x][y] = (Cell*) malloc(sizeof(Cell));
                }// for
        }// for

        for (int x = 0; x < 10; x++) {
                for (int y = 0; y < 10; y++) {
                        playingArea[x][y]->isMineHere = 0;
                        playingArea[x][y]->x = x;
                        playingArea[x][y]->y = y;
                        playingArea[x][y]->counterSurr = -1;
                }// for
        }// for

        for (int x = 0; x < 10; x++) {
                for (int y = 0; y < 10; y++) {
                        printf("%d", playingArea[x][y]->isMineHere);
                }// for
                printf("\n");
        }// for


        for (int x = 0; x < 10; x++) {
                for (int y = 0; y < 10; y++) {
                        delete( playingArea[x][y] );
                }// for
        }// for

    //std::cout << "Hello World!\n";
}// main
 
Nahoru Odpovědět
28.12.2019 17:40
Avatar
Martin Dráb
Tvůrce
Avatar
Odpovídá na JerryM
Martin Dráb:29.12.2019 19:22

ježiš ty seš ale puntičkář ....

A pojďme řady takových rozmnožit! :-)

tak by si místo Bool dal Char .... to je to samý ...

Teoreticky tím dovolíš překladači provádět více věcí co se týče analýzy kódu (prostě mu řekneš, že hodnota dané proměnné má vlastně dva významy). Ale taky bych použil asi (unsigned) char, protože nemám rád používání velkého množství knihovních hlavičkových souborů :-).

více proměnných ve struktuře taky nevadí

Tady by možná bylo zajímavé vytvořit nějaký opravdu tvrdý paměťový test za bonusové body, který by opravdu vyžadoval např. 1 bajt na položku pole, rozhodně ne cca 12-16.

a to pole si definuje s nějakým max. rozměrem, kterej se pak už nesmí překročit no ...

Na tohle má odpověď přímo zadání; statická alokace znamená buď selhání na velkých vstupech (přetečení bufferu), nebo na malých (překročení paměťový limitu).

je to školní úloha.... přece nebude používat realoc ...

Stále je možné jej nahradit kombinací volání free a malloc. Jestli je cílem zadání procvičit práci s dynamickou pamětí, tak proč ne. Tady tedy úplně nechápu, proč se alokuje každá buňka zvlášť, když plán je používat jejich řádky. Takže proč nealokovat rovnou celý řádek najednou...

Školní úloha to sice je, ale proč ji nevyužít k naučení se správných postupů, jako třeba odstranění zbytečné redundance či ošetření případu, kdy alokace paměti selže (pro C). Není nutné mít 4 bity na políčko, 8 (unsigned char) klidně stačí.

tak by to vypadalo nějak takhle no

Dá se zbavit hlavičkového souboru iostream (a tedy nutnosti použít C++), když už (téměř) všude navrhuješ printf. Osobně jej také preferuji – sice by akademici řekli, že není typově bezpečné, ale formát výstupu je ze zdrojáku vidět na první pohled. Navíc, dnes jsou schopné překladače ověřit, že do printf a jemu podobných vstupují parametry s typy odpovídající formátovacímu řetězci.

Nahoru Odpovědět
29.12.2019 19:22
2 + 2 = 5 for extremely large values of 2
Avatar
JerryM
Člen
Avatar
Odpovídá na Martin Dráb
JerryM:29.12.2019 21:26

vy ste fakt strašný prudiči ...
taková drsná kritika
je to céčko na Windows nejsme v 80 letech na Atari ... bajt sem bajt tam ...
céčko neni assembler kde se honí každej bit
tak ona selže alokace paměti pro 1 Kilo Bajt jo ? to sou ale starosti ....
už vám někdo řek že v céčku se dá všechno napsat na tisíc způsobů ?
důležitá je přehlednost ... logický sousled... :))))))))))))
chápete jo ?

 
Nahoru Odpovědět
29.12.2019 21:26
Avatar
DarkCoder
Člen
Avatar
Odpovídá na JerryM
DarkCoder:29.12.2019 21:58

Dost už toho přede mnou napsal Martin, přesto se k tomu také vyjádřím.

ježiš ty seš ale puntičkář ....

Ano, preciznost a smysl pro detail je vlastnost kterou zastávám. I přesto, že HW poskytuje vysoký výkon, je pro mě optimalizace aplikace důležitá.

tak by si místo Bool dal Char .... to je to samý …

Smyslem bylo poukázat na to, že byte ani boolean nejsou platnými typy v C. Jinak datové typy _Bool a char bych za sebe rozhodně nezaměňoval. Proměnnou typu _Bool nelze v některých místech použít tam kde lze použít proměnnou typu char. Pro mě má datový typ char mnoho uplatnění. Ani já kvůli uložení boolovské hodnoty nepoužívám typ _Bool, používám char u běžných aplikací, bitové pole u výkonných aplikací. (Do proměnné typu char lze uložit až 8 booleovských stavů). Také nevkládám další hlavičkový soubor kvůli jednomu typu který si mohu nasimulovat jinak. Pokud chci aby byl program lépe čitelný, použiji typedef pro definování vlastního typu, popř. proměnnou doplním o prefix b.

více proměnných ve struktuře taky nevadí

Nevadí, ale proč tam dávat něco navíc co mi akorát sníží efektivitu aplikace. Budu-li brát v potaz úlohu ze zadání, pak při malém počtu vstupních dat to až tak nevadí. Co se ale stane když těch dat bude podstatně více. Zde už pak rozdíl, zda struktura obsahuje jeden prvek nebo víc, je znatelná. Jestli velikost struktury po použití sizeof bude 1byte nebo 10bytů, to už bude také znát. Mít aplikaci, která bude na na paměť podstatně méně náročnější (10x) a také rychlejší (méně strojových instrukcí z důvodu méně častého zápisu do proměnné), je fajn. Že se to dá napsat ještě úsporněji za pomoci bitových polí, ano, ale zase to jde na úkor rychlosti psaní aplikace a čitelnosti. Uložení do typu signed char nebo unsigned char je plně dostačující.

a to pole si definuje s nějakým max. rozměrem,

Pro tuto úlohu staticky alokovat 2D pole, jak už zadání napovídá, není správné. Plýtvání pamětí nebo naopak nemožnost řešit úlohu u zadaného většího množství dat je úděl statické alokace.

je to školní úloha.... přece nebude používat realoc ... .

Smyslem úlohy bylo procvičení si práce s dynamickou alokací v C. Použití realloc() se tedy nabízí jako dobrá volba. Ano, já bych realloc() v tomto případě také nepoužil, obešel bych to přes souborový systém kde bych poté dynamicky alokoval 2D pole najednou.

tak by to vypadalo nějak takhle no

Jak je ze zadání patrné, je úloha určena na procvičení dynamické alokace v C a tak bych to celé psal v C. Míchání iostream spolu s printf() nepůsobí zrovna dobře a už vůbec ne míchání malloc() s delete. O nutnosti použití dynamické alokace již byla v mém příspěvku řeč.

Nahoru Odpovědět
29.12.2019 21:58
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
JerryM
Člen
Avatar
JerryM:30.12.2019 9:05

ne ne ... já musim mít poslední slovo :)))))))))) protože sou Vánoce :)))))))

 
Nahoru Odpovědět
30.12.2019 9:05
Avatar
JerryM
Člen
Avatar
Odpovídá na JerryM
JerryM:30.12.2019 9:31

tady je info o typu proměnné bool:

https://www.geeksforgeeks.org/bool-in-c/

 
Nahoru Odpovědět
30.12.2019 9:31
Avatar
JerryM
Člen
Avatar
Odpovídá na JerryM
JerryM:30.12.2019 9:34

a tady se o tom píše taky:
https://sites.uclouvain.be/…dbool.h.html

 
Nahoru Odpovědět
30.12.2019 9:34
Avatar
JerryM
Člen
Avatar
Odpovídá na JerryM
JerryM:30.12.2019 9:35

nechápu proč nikdo z vás sem nedal ani řádku ... klidně ste mohli celej kód napsat ...

 
Nahoru Odpovědět
30.12.2019 9:35
Avatar
JerryM
Člen
Avatar
JerryM:30.12.2019 10:31

Co to znamená věta "....no, já bych realloc() v tomto případě také nepoužil, obešel bych to přes souborový systém kde bych poté dynamicky alokoval 2D pole najednou...." ?????????
to nedává smysl :)))))))))) njn sou vánoce ....

Alokovat celé pole najednou znamená nepoužít v něm záznamy, ale jen typ Int, aby -1 znamenalo mina a 1 až 8 počet min v 8mi okolí když tam mina neni.....

 
Nahoru Odpovědět
30.12.2019 10:31
Avatar
DarkCoder
Člen
Avatar
Odpovídá na JerryM
DarkCoder:30.12.2019 15:52

Co to znamená věta "....no, já bych realloc() v tomto případě také nepoužil, obešel bych to přes souborový systém kde bych poté dynamicky alokoval 2D pole najednou...." ?????????

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

#define FILENAME "mines.dat"

int main(void) {

        FILE *fp = NULL;
        int c;
        int iFilelen = 0;
        int iNewline = 0;
        int iNewlinepos = 0;
        int i, j, iCol, iRow;
        unsigned char **ucMatrix = NULL;

        fp = fopen(FILENAME, "w");
        if (!fp) {
                fprintf(stderr, "Chyba pri vytvareni souboru\n");
                exit(1);
        }

        do {
                c = getchar();
                if (c == EOF) break;
                fputc(c, fp);
                if (ferror(fp)) {
                        fprintf(stderr, "Chyba pri zapisu do souboru\n");
                        exit(2);
                }
                iFilelen++;
                if (c == '\n') {
                        if (!iNewline) iNewlinepos = iFilelen;
                        iNewline++;
                }
        } while (1);

        if (fclose(fp)) {
                fprintf(stderr, "Chyba pri uzavirani souboru\n");
                exit(3);
        }

        // Test validace pro vytvoreni matice
        if ((iNewline * iNewlinepos != iFilelen) || (!iFilelen)) {
                puts("Nespravny vstup");
                exit(0);
        }

        iCol = iNewlinepos;
        iRow = iFilelen / iCol;

        ucMatrix = (unsigned char **) malloc(iRow * sizeof(unsigned char *));
        for (i = 0; i < iRow; i++) {
                ucMatrix[i] = (unsigned char *)malloc(iCol * sizeof(unsigned char));
        }

        fp = fopen(FILENAME, "r");
        if (!fp) {
                fprintf(stderr, "Chyba pri otevirani souboru\n");
                exit(4);
        }

        for (i = 0; i < iRow; i++) {
                for (j = 0; j < iCol; j++) {
                        c = fgetc(fp);
                        if (ferror(fp)) {
                                fprintf(stderr, "Chyba pri cteni ze souboru\n");
                                exit(5);
                        }
                        ucMatrix[i][j] = (unsigned char) c;
                }
        }

        if (fclose(fp)) {
                fprintf(stderr, "Chyba pri uzavirani souboru\n");
                exit(6);
        }

        if (remove(FILENAME)) {
                fprintf(stderr, "Chyba pri odstranovani souboru\n");
                exit(7);
        }

        // Zde dalsi testy validace a modifikace obsahu matice
        // ...

        putchar('\n');
        for (i = 0; i < iRow; i++) {
                for (j = 0; j < iCol - 1; j++) {
                        putchar(ucMatrix[i][j]);
                }
        }

        for (i = 0; i < iRow; i++) free((void *)ucMatrix[i]);
        free((void *)ucMatrix);

        return 0;
}

Jak je z výše uvedeného programu vidět, použití funkce realloc() skutečně není třeba. Na druhou stranu toto řešení postrádá smysl zadání úlohy, neboť si né úplně procvičuji práci s dynamickou alokací paměti. :-)

Alokovat celé pole najednou znamená nepoužít v něm záznamy, ale jen typ Int, aby -1 znamenalo mina a 1 až 8 počet min v 8mi okolí když tam mina neni.....

Při dynamické alokaci pole lze samozřejmě použít strukturovaný typ stejně tak jako jakýkoli základní datový typ. Hodnotu -1 lze uložit i do datového typu char, ne jen int. Ač bývá datový typ char defaultně jako signed, je ale lepší překladači tuto informaci jasně předat a použít modifikátor datových typů signed. Jelikož označení pro minu, prázdné pole a hodnoty 0-8 jde reprezentovat pomocí datového typu char, je další modifikace matice zbytečná.

Nahoru Odpovědět
30.12.2019 15:52
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
JerryM
Člen
Avatar
JerryM:30.12.2019 16:18

ježiš to je ale složitý ...

 
Nahoru Odpovědět
30.12.2019 16:18
Avatar
DarkCoder
Člen
Avatar
DarkCoder:30.12.2019 16:20

Ještě jedna důležitá věc. V programu chybí test na to, zda bylo přidělení paměti úspěšné. Nelze se spoléhat na to, že počítač má dostatek paměti. :-)

Nahoru Odpovědět
30.12.2019 16:20
"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 JerryM
DarkCoder:30.12.2019 16:23

Pokud má být program robustní, nelze opomenout ladění. To že toho je polovina programu, to se nedá nic dělat. :-)

Nahoru Odpovědět
30.12.2019 16:23
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
JerryM
Člen
Avatar
JerryM:30.12.2019 16:25

a proč vlastně používáš zápis vstupu do souboru ? když se něco posere tak je to stejný jako když se posere čtení z klávesnice přímo do paměti.
a taky by se dal alokovat celej prostor rovnou bez separátní alokace sloupců a knim pak řádků a pak k němu přistupovat pointerovou aritmetikou ... schválně kdo vymyslí složitější variantu .. :)))

 
Nahoru Odpovědět
30.12.2019 16:25
Avatar
JerryM
Člen
Avatar
JerryM:30.12.2019 16:27

teď mě napadá ty alokuješ paměť s parametrem "unsigned char" ale má tam bejt i -1 když tam na tom políčku je mina ... takže by tam spíš měl bejt signed char ne ? aby to šlo: -1, 0,1, 2,3,4,5,6,7,8, jako počet min v okolí prázdného místa.

 
Nahoru Odpovědět
30.12.2019 16:27
Avatar
DarkCoder
Člen
Avatar
Odpovídá na JerryM
DarkCoder:30.12.2019 17:48

a proč vlastně používáš zápis vstupu do souboru ?

Správná otázka, vysvětlím. Tento způsob jsem nezvolil jen tak abych prezentoval jinou možnost, jak úlohu řešit. Souborový systém mi zjednodušuje práci. Když nevím, kolik dat budu potřebovat na vstupu přijmout, pak nevím, kolik paměti si mám připravit. Zadání úlohy eliminuje použití některých principů (seznam, apod.). U způsobu za pomocí realloc() si určuji nějaké množství počáteční paměti. Při přebírání dat na vstupu neustále testuji, zda-li paměť, kterou jsem si stanovil je postačující. Popřípadě měním velikost dle potřeby. Na konci se mohu rozhodnout, zda-li se alokovanou paměť ponechám a nebo snížím na požadovaný počet. Když se zvolí malý díl paměti pro navýšení, nespotřebuji tolik paměti, ale zase mohu volat realloc() častěji což může vést ke zpomalení aplikace. Teď si možná říkáš, je to pár kB, za to to nestojí, prostě alokuji pořádný kus a neřeším to. Vše se mění s datovým typem, respektivě s jeho rozsahem. Když chci uložit proměnnou o velikosti 1bytu (char), tak pokud budu mít nadbytek paměti pro 1000 dalších až tak nevadí. Ale když už pak prvkem pole bude rozsáhlá struktura, pak už velikost paměti zbytečně alokované bude o dost znatelnější. Toto "dýchání" pole si bere dost strojových instrukcí.

Při použití souborového systému si nemusím alokovat z počátku nic (To si řeší systém). Vše si uložím do souboru a systém za mě zařídí vše ostatní. Mě pak stačí zjistit velikost souboru, což mi značí, kolik mám alokovat paměti. To mi umožňuje alokovat právě tolik paměti, kolik potřebuji. Neplýtvám ani ji nemám nedostatek. Ideální. Ano, i souborový systém může selhat, proto je tak nesmírně důležité ošetřit, zda nedošlo k chybě. Samožřejmě zápis do souboru a následné čtení a zapsání do paměti také něco stojí. Zde by určitě mohlo být zajímavé a porovnat tyto způsoby. Z důvodu menšího počtu manipulací s celým polem by to ale mělo být rychlejší než n-krát používané realloc().

Když už se zmínila pointerová aritmetika, bylo by její použití na místě. Dříve bylo použití pointerové aritmetiky rychlejší nežli indexace pole. Troufám si tvrdit, že dnešní překladače dokáží optimalizovat kód a zaměnit indexaci polí za použití s ukazateli.

teď mě napadá ty alokuješ paměť s parametrem "unsigned char" ale má tam bejt i -1 když tam na tom políčku je mina ... takže by tam spíš měl bejt signed char ne ? aby to šlo: -1, 0,1, 2,3,4,5,6,7,8, jako počet min v okolí prázdného místa.

Na toto jsem již reagoval v předchozím příspěvku. Takže jen doplním a upřesním. S hodnotou -1 nakonec vůbec nepracuji. Unsigned char je správně. Zadání jasně říká, políčko s minou je označeno jako hvězdička, prázdné políčko jako tečka. Všechna tato označení i číselným ohodnocením, lze reprezentovat pomoci unsigned char. Tudíž je zbytečné provádět další modifikace minového pole. Je třeba pouze provést různé validace na platný vstup.

Nahoru Odpovědět
30.12.2019 17:48
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
JerryM
Člen
Avatar
JerryM:30.12.2019 19:19

tý jo to je přímo vědecký vysvětlení... tomu se nedá oponovat :)))))))))

 
Nahoru Odpovědět
30.12.2019 19:19
Avatar
DarkCoder
Člen
Avatar
Odpovídá na JerryM
DarkCoder:31.12.2019 2:51

Spousta toho se dá ještě zlepšit. V programu je vidět, že je vše zapisováno nebo čteno po jednom znaku. Data lze číst nebo zapisovat z nebo do souboru naráz, což o dost urychlí aplikaci. Třeba úryvek kódu pro načtení vstupu by mohl být pozměněn tak, že by obsahoval pomocný buffer, do kterého se data načtou a po jeho zaplnění se obsah zapíše do souboru. Pro čtení lze použít getchar() nebo fgets(). Zápis bufferu do souboru lze udělat přímo pomocí fwrite():

do {
        // Zde nacteni do bufferu dokud není EOF

        // pri zaplneni vypis do souboru
        if (iBuffpos > BUFFSIZE) {
                fwrite(buffer, sizeof(uchar), BUFFSIZE, fp);
                iFilelen += BUFFSIZE;
                iBuffpos = 0;
        }
} while (1);
// Zapsani do souboru zbytku bufferu
fwrite(buffer, sizeof(uchar), iBuffpos, fp);
iFilelen += iBuffpos;

kde proměnné, konstanty a typy jsou definovány následovně:

typedef unsigned char uchar;
#define BUFFSIZE 1024
uchar buffer[BUFFSIZE];
int iBuffpos = 0;

Alokace minového pole lze zjednodušit za pomocí single pointeru. Pouze se pak k dané hodnotě musí přistupovat dopočtem pomocí ukazatelové aritmetiky. Na druhou stranu to je o dost přehlednější.

uchar *ucMatrix = NULL;

ucMatrix = (uchar*)malloc(iFilelen * sizeof(uchar));
if (!ucMatrix) exit(1);

free((void *)ucMatrix);

Načtení celého obsahu souboru do 2D pole v jednom kroku:

fread(ucMatrix, sizeof(uchar), iFilelen, fp);

A nakonec výpis obsahu 2D pole na obrazovku v jednom kroku:

fwrite(ucMatrix, sizeof(uchar), iFilelen, stdout);

Pro použití funkcí fread() a fwrite() je třeba otevírat soubor v binárním režimu.
Teď je to ještě jednodušší a efektivnější.. :-)

Nahoru Odpovědět
31.12.2019 2:51
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
JerryM
Člen
Avatar
JerryM:3.1.2020 17:55

hmmm tý jo :)

 
Nahoru Odpovědět
3.1.2020 17:55
Avatar
DarkCoder
Člen
Avatar
DarkCoder:3.1.2020 20:34

Když chci šetřit pamětí, tak stačí jeden bit (ne byte) na to, abych zjistil, zda na políčku je mina.

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

#define ROW 4
#define COL 5

typedef struct {
        unsigned val : 1;
} bCELL;

int main(void) {
        bCELL *bMatrix = NULL;

        srand((unsigned int)(time(NULL)));

        bMatrix = (bCELL*)malloc(ROW * COL * sizeof(bCELL));
        if (!bMatrix) exit(1);

        // generovani obsahu minoveho pole
        for (int i = 0; i < ROW; i++) {
                for (int j = 0; j < COL; j++) {
                        (*(bMatrix + i * COL + j)).val = rand() % 2;
                }
        }

        // vypis minoveho pole
        for (int i = 0; i < ROW; i++) {
                for (int j = 0; j < COL; j++) {
                        (*(bMatrix + i * COL + j)).val ? putchar('*') : putchar('.');
                }
                putchar('\n');
        }

        free((void *)bMatrix);

        return 0;
}

pro zjednodušení byly stanoveny rozměry a obsah minového pole explicitně..

Na druhou stranu program řešený tímto způsobem poběží pomaleji neboť je třeba vstup konvertovat na hodnoty 0 nebo 1 a poté je převádět zpět na znak. Nechť si každý vybere dle vlastních priorit, zda rychlost nebo nižší paměťová náročnost. :-)

Nahoru Odpovědět
3.1.2020 20:34
"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 39 zpráv z 39.