Pouze tento týden sleva až 80 % na e-learning týkající se PHP. A zároveň využij akce až 30 % zdarma při nákupu e-learningu. Více informací.
Hledáme asistenty pro kurzy programování - pohodová brigáda. Více info
discount 30
Avatar
Michal H.
Člen
Avatar
Michal H.:24.11.2020 5:28

Ahoj,
jedu teď c++ a sfml podle knížky. Narazil jsem na něco co je sice banální kravina, ale furt mě to nějak provokuje (takovej ten hlásek někde vzadu v hlavě ). Jde o funkci která navyšuje hodnotu I o X a I nesmí přesáhnout maximální hodnotu. Řešení z knihy

void funkce (int x){
        i += x;
        if ( i >  i_max)
              i = i_max;

Zkusil jsem: části kódu si píšu dopředu sám, abych jen tupě neopisoval. Tak že moje řešení je

void funkce (int x ){
        if ( i + x < i_max ){
            i +=x
        }
       else{
              i = i_max
        }

Chci docílit: Jako hobík a samouk jsem v rozpacích.
Na jednu stranu myslím že funkce z knihy je efektivnější - Moje funkce musí vykonat výpočet a porovnání navíc.
Na stranu druhou si myslím že moje funkce je bezpečnější. - Nedovolí přesažení maximální hodnoty ani v jedné části. Funkce z knihy přesažení max. hodnoty chrání jen část kódu která závisí na jejím přesažení ( podle mě místo potencionální chyby )

Chtěl bych jen vědět zda se mé úvahy odebírají správným směrem. A jestli je nějaké obecné pravidlo kterého se držet při navrhování funkcí.
Je mi jasné že dopad na výkon mezi těmito funkcemi v dnešní době bude asi naprosto zanedbatelný, ale rád bych dělal věci správně i u složitějších věcí.

 
Odpovědět
24.11.2020 5:28
Avatar
Michal H.
Člen
Avatar
Odpovídá na Michal H.
Michal H.:24.11.2020 5:51

edit: koukám že mi v kódu chybí středníky, ale o ty teď tak úplně nejde :)

 
Nahoru Odpovědět
24.11.2020 5:51
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Michal H.
DarkCoder:24.11.2020 9:39

Ani jedna funkce nebude pracovat správně ve všech případech a ani jedna funkce není koncipována správně. Avšak obě funkce budou fungovat v programu správně, první funkce bude efektivnější nežli druhá. Pojďme se podívat proč tomu tak je.

Taková funkce by mohla sloužit pro závodní hru kde po stisku potažmo držení konkrétní klávesy by mohla simulovat přidání plynu a tím i navýšení rychlosti až do maximálně stanovené rychlosti.

Ani jedna funkce nebude pracovat správně ve všech případech:

Datové typy mají své rozsahy. Překročením rozsahu vzniká nesmyslná hodnota, která může mít za následek chybnou funkci programu. Podívejme se na simulace obou funkcí:

#include <stdio.h>
#include <limits.h>

#define MAX_SPEED 100
#define DELTA 10

int main(void) {
        int speed;

        // Simulace 1. funkce
        speed = INT_MAX;

        speed += DELTA;
        if (speed > MAX_SPEED) speed = MAX_SPEED;
        printf("%d\n", speed);

        // Simulace 2. funkce
        speed = INT_MAX;

        if (speed + DELTA < MAX_SPEED) speed += DELTA;
        else speed = MAX_SPEED;
        printf("%d\n", speed);

        return 0;
}

Výsledkem obou simulací je nesmyslná hodnota. Ta vznikla přetečením rozsahu typu. Je na programátorovi, aby funkci ošetřil tak aby byla použitelná v programu.

Avšak obě funkce budou fungovat v programu správně:

Je to proto, že hodnota rychlosti v programu nebude dosahovat tak vysoké hodnoty, při kterém by program nefungoval správně. Ani posun rychlosti nebude velký.

Funkce budou pracovat bude-li platit:

speed <= INT_MAX - DELTA

První funkce bude efektivnější nežli druhá:

První funkce bude vykonávat přiřazení a porovnání, popř. přiřazení, porovnání a přiřazení.
Druhá funkce má navíc vyhodnocení aritmetického výrazu.

Ani jedna funkce není koncipována správně:

Pokud funkce obsahuje proměnnou, měla by tato proměnná být předávána funkci jako její argument. Jelikož se mění, měl by být funkci předáván ukazatel na tuto proměnnou. Takto jsou funkce koncipovány tak že využívají pro svůj chod globální proměnnou a funkce se tak stává závislá na něčem co je venku a tudíž je tento koncept chybný. Pokud je i_max další proměnná, platí totéž co pro předchozí proměnnou s tím rozdílem, že nemusí být předáván ukazatel. Použití i_max by bylo v pořádku, pokud by se jednalo o makro. V takovém případě bych použil pro makro velká písmena.

Závěrem:

Ač ani jedna funkce není dokonalá, lze obě funkce v programu použít. Z důvodu efektivity je lepší použít první funkci.

Nahoru Odpovědět
24.11.2020 9:39
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Michal H.
Člen
Avatar
Odpovídá na DarkCoder
Michal H.:24.11.2020 16:35

Díky za odpověď. Hned mám zase nad čím přemýšlet ;)

A díky za připomenutí o přetečení typu. Jak si člověk patlá furt jen tak něco pro sebe, tak na tohle moc nemyslí. Příklad s rychlostí byl inspirující :)

Proměnná i_max není macro... Celkově se jedná o metodu třídy. Ve které:
i - jsou aktuální životy.
i_max - je max. životu ( mění se v průběhu hry v závislosti na vylepšení)
x - je parametr/argument metody třídy která přidává x života ( spuštěno při léčení )

Ve třídě jsou samozřejmě proměnné popsány inteligentněji.

Záměrně jsem to nějak nerozepisoval. A zjednodušil jsem to takto. Spíše mi šlo o to co je při návrhu správně ( resp. jak správně postupovat ). Protože z mého pohledu je správně když mám danou maximální hodnotu pro nějakou proměnnou která tuto hodnotu nesmí přesáhnout. Tak nejdříve zjistím zda zvýšení proměnné o x nezpůsobí přesah pokud ne, tak změním onu proměnnou.
Na stranu druhou je původní funkce (1) asi efektivnější, ale z mého pohledu ne úplně bezpečná.

Když pominu pokročilejší věci v c++. Je to jako když si rychle napíšeš funkci na procházení int pole.

void funkce ( int * pole, int velikost_pole ){

            for (int i = 0; i < velikost_pole; i++){
                        ...;
                }

Je to rychlé a funguje to. Jen by to jaksi chtělo minimálně zkontrolovat zda velikost_pole není menší než 1. Pokud si něco takového napíšu narychlo pro sebe abych něco ověřil etc. Asi je to OK. Ale všimnul jsem si že to pak dělám i přímo ve svých projektech. Když je to rychlé a funguje to... :) Sic si programuji jen pro sebe, ale chtěl bych to dělat pořádně. Poslední dobou se snažím i pomocné funkce psát pečlivěji. Proto mě příklad z knihy ( funkce 1) trochu překvapil.

Jaká je vlastně hranice mezi bezpečným a efektivním kódem? Do jaké míry je ověření vstupu únosné a kdy už jde o paranoiu? :)

A jak jsi v závěru napsal:

Ač ani jedna funkce není dokonalá, lze obě funkce v programu použít. Z důvodu efektivity je lepší použít první funkci.

Ze své skromné zkušenosti právě důvod efektivity ( teda alespoň podle mě to efektivní vždy bylo :D ) mě stál spoustu "UAAAAAAA" a bolestí hlavy. :) Pravda u mě to je i dostatkem neskromné neznalosti a nezkušenosti.

 
Nahoru Odpovědět
24.11.2020 16:35
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Michal H.
DarkCoder:24.11.2020 21:16

K přetečení rozsahu typu nedochází tak často. Vyjímkou je typ char, zejména při použití modifikátorů signed a unsigned. Funkce by měly být jednoduché, ošetření platnosti vstupů by mělo být pokud možno řešeno vně funkce. U práce s poli zejména, kde dochází k traversování.

Robustnost aplikace je vždy na prvním místě, optimalizace je vždy bonus. Obě kritéria spolu souvisí, nesmyslné testování může podstatně snížit výkon aplikace. Kdy testovat a kdy ne, k tomu se postupně dopracuješ jak budeš získávat zkušenosti. Jsi části, kdy testování je důležité, opomenutí může způsobit pád programu, jehož příčina může být někdy obtížně dohledatelná.

Je správné, že neopomínáš ladění aplikace. Je to jedna z nejnáročnějších činností, ale nesmírně důležitá.

Nahoru Odpovědět
24.11.2020 21:16
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
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 5 zpráv z 5.