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.


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.
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...?
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.
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)
+20 Zkušeností
+2,50 Kč

Patrik Valkovič:30.11.2019 14:51
To není úplně pravda, i z konzole lze vyvolat EOF. Data jdou ze standardního vstupu.
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.
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
Erik Šťastný:6.12.2019 12:24
Můžu prosím ze zvědavosti o jaký semestr FITu, jde?
Lu Kiss:7.12.2019 10:54
Co jsem tak slyšel, tak slavný Vagnerův Progtest na C je v prvnim semestru
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í
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ší.
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
Dobrý den,
Myslím si že toto asi nebude nejlepší řešení Vašeho problému.
Stavte se na konzultaci po prosemináři.
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ý ....
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.
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
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.
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 ?
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č.
JerryM:30.12.2019 9:34
a tady se o tom píše taky:
https://sites.uclouvain.be/…dbool.h.html
JerryM:30.12.2019 9:35
nechápu proč nikdo z vás sem nedal ani řádku ... klidně ste mohli celej kód napsat ...
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.....
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á.
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.
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 ..
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.
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ší..
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.
Zobrazeno 39 zpráv z 39.