přejít na obsah přejít na navigaci

Linux E X P R E S, Programovanie v jazyku C++: Bloky a reverzný reťazec

Programovanie v jazyku C++: Bloky a reverzný reťazec

cplusplus.png

V článku na vás čakajú bloky a ukážeme si platnosť premenných v bloku a mimo neho. Taktiež si vyskúšame algoritmus na reverzný reťazec.


Bloky

V jazyku C++ môžeme definovať pomocou {} blok, v ktorom môžeme deklarovať lokálne premenné. Inak povedané, premenná je platná len vo vnútri bloku. Ak sa končí náš blok, definovaná premenná stráca platnosť. Prvý príklad nám fungovanie bloku priblíži viac.

Ešte poznámka, bloky sa využívajú všade. Poznáte ich z cyklov či podmienok. Teraz sa ale zameriame na platnosť samotných premenných. Taktiež by vám to malo zodpovedať otázku, prečo (väčšinou, podľa potreby) nedeklarujeme premenné v aktuálnom dosahu bloku, ale mimo nich (alebo výstižnejšie, nad vyššou úrovňou ako je aktuálny blok).

1. príklad: Využitie bloku

#include <iostream>
 
 using namespace std;
 
 int main()
 {
     int vaha = 55;
 
     {
         int vyska = 189;
         cout << "Moja vyska je: " << vyska << " a vaha: " << vaha << "\n";
     }
 
     return 0;
 }

Výsledná činnosť programu:

Moja vyska je: 189 a vaha: 55

Rozbor programu:

Vidíte, náš definovaný blok kódu funguje. Vo vnútri sme si vytvorili svoju premennú a následne sme ju vypísali. Ale, akú máme záruku, že premenná vyska je prístupná i mimo bloku?

Presvedčme sa v ďalšom príklade.

2. príklad: Skúsme oproti prvému príkladu šupnúť výpis premennej vyska mimo pôsobností bloku

#include <iostream>
 
 using namespace std;
 
 int main()
 {
     int vaha = 55;
 
     {
         int vyska = 189;
     }
 
     cout << "Moja vyska je: " << vyska << " a vaha: " << vaha << "\n";
 
     return 0;
 }

Výsledná činnosť programu:

vaša cesta k súboru||In function 'int main()':|
 vaša cesta k súboru|8|warning: unused variable 'vyska' [-Wunused-variable]|
 vaša cesta k súboru|10|error: 'vyska' was not declared in this scope|
 ||=== Build failed: 1 error(s), 1 warning(s) (0 minute(s), 1 second(s)) ===|
 Alebo iný podobný výpis, avšak všetky výpisy majú spoločné slovo error

Rozbor programu:

Čo sme pokazili? Len to, že sme k lokálne deklarovanej premennej vyska chceli pristúpiť mimo nášho bloku definovaného {}. Všimnite si ale, že premenná vaha má prístup k celému kódu. Aj v bloku. Pretože premenná váha je súčasťou dobre známej hlavnej funkcie main. Premenné v main majú prístup do celého bloku, ale mimo funkcie main nemajú.

Na záver tejto témy si dajme ešte jeden príklad.

3. príklad:

#include <iostream>
 
 using namespace std;
 
 int main()
 {
     int vaha = 65;
 
     {
         int vaha = 109;
     }
 
     cout << "Moja vaha je: " << vaha << "\n";
 
     return 0;
 }

Výsledná činnosť programu:

Moja vaha je: 65

Rozbor programu: Výsledok je vám jasný? Stále pamätajte, kde je pôsobnosť deklarovanej premennej v bloku.



Reverzný reťazec

V minulých dieloch sme si ukázali ako jednoducho vypísať pole a reťazec obrátene. Teraz to skomplikujme a napíšeme obratené reťazec pomocou výmeny písmen. Nazveme to reverzný reťazec. Využijeme pri tom knižnicu <string>, ktorá je ušitá presne pre potreby jazyka C++.

4. príklad: Reverzný reťazec

 #include <iostream>
 #include <string>

 using namespace std;

 int main()
 {
    string word;

    cout << "Napiste lubovolne slovo: ";
    getline(cin, word);

    if (word.empty())
    {
        cout << "Sorry, ale musis nieco zadat! Skus znova.";
        return 0;
    }

    char znak;
    int i, j;

    for (i = 0, j = word.size() - 1; i < j && j > 0; i++, j--)
    {
        znak = word[j];
        word[j] = word[i];
        word[i] = znak;
    }
    cout << "Vas zadany retazec po reverzii je: " << word << "\n";
    return 0;
 }

Výsledná činnosť programu:

Napiste lubovolne slovo: maminka
Vas zadany retazec po reverzii je: aknimam

Rozbor programu: Už hneď si všimnete, že tento program je napísaný profesionálnejšie a snáď tak uspokojím i náročnejších čitateľov. Zároveň je to ľahko pochopiteľný algoritmus. Miesto zbytočného písania dvoch cyklov for, sme využili možnosť to skĺbiť v jedno napísanie cyklu for. Algoritmus funguje tak, že vymieňa jednotlivé písmená z oboch strán v reťazci. Áno, to som napísal dosť krkolomne, preto radšej ukážem príklad:

Máme slovo maminka, ako som zadal už v bežiacom programe.

Vo slove maminka najprv premenná i zoberie prvé písmeno m a premenná j zoberie posledné písmeno a. Následne algoritmus prehodí oba písmena. A takto to pokračuje ďalej. Ukážka:

1. iterácia: maminka po vykonaní aaminkm

2. iterácia: aaminkm po vykonaní akminam

3. iterácia: akminam po vykonaní aknimam

4. iterácia: sa nevykoná, pretože je porušená podmienka i<j




Nahoru

Příspěvky

Programovanie v jazyku C++: Bloky a reverzný reťazec
Daniel Kozak 22. 12. 2016, 12:16:02
Odpovědět  Odkaz 
Hned prvni odstavec opet obsahuje nepresnosti, doporucuji autorovi se podivat na rozdil mezi lifetime a scope visibility
Programovanie v jazyku C++: Bloky a reverzný reťazec
Jardík 22. 12. 2016, 21:38:38
Odpovědět  Odkaz 
Když už článek, který má asi někoho učit, tak alespoň použijte správné datové typy a neučte chyby.
> for(i = 0, j = word.size() - 1; i < j; i++, j--)
1) string::size() vrací hodnotu typu string::size_type, nikoliv int.
2) Pokud word.size() je 0, tak po odečtení 1 máte std::numeric_limits::max(), a to po uložení do proměnné typu int může způsobit (v závislosti na velikosti datových typů) UB při přetečení intu
3) Takovému špatně napsanému cyklu se dá vyhnout použitím std::reverse()
4) Vaše definice reverzního řetězce je asi chybná. Ve skutečnosti je tento problém o dosti komplexnější (vícebytová kódování a pak ještě problematika grafémů). Místo na řetězci by bylo lepší algoritmus předvést třeba na poli nějakých čísel.

Článek má velmi nízkou úroveň a někdo s Vašimi znalostmi (bez urážky) by je neměl vydávat. Akorát snižují gramotnost programátorů, kteří se pak dopouští různých bezpečnostních chyb (způsobené třeba hloupým přetečením intu a nějaké optimalizace kompilátoru, která předpokládá, že přetéct nesmí).
Eduard Boldižár Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Eduard Boldižár 22. 12. 2016, 21:58:19
Odpovědět  Odkaz 
Je to článok pre začiatočníkov, nie pre bezpečnostných expertov. V každom možnom anglickom tutoriali sa stretnete s týmto typom príkladov ako vhodné pre začiatok, takže urážlivý tón si odpustte. Teóriu i príklady vhodné pre začiatočníkov pozorujem a inšpirujem z viacerých zdrojov, takže to nie sú len moje výmysly z hlavy. Ak príde čas v budúcností riešiť tieto bezpečnostné problémy, tak prídu. Teraz je začiatočník rád, že začína chápať, ako to asi funguje. Nerieší krajné záležitostí, ktoré môžu nastať. Ani žiadne výnimky, modely a ďalšie pokročilé nástroje, ktoré sú vhodné skôr už pre zabehnutých programátorov. Pre nich tento seriál určite nie je.
Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Zbyňa 15. 01. 2017, 22:39:37
Odpovědět  Odkaz 
a ja myslel že string::size() vrací délku řetězce hmmm tak jak to teda je?? jak zjistím délku svého řetězce například std::string defaultSizeStringMessage = "zdarjaksvina";
// size_t delka = defaultSizeStringMessage.size()
pozn kašlu na to že je dole odpověď a to docela kvalitní četl jsem vše.. Jardíku děkuji nádherné vysvětlení a použití numeric limits.. ale otázka tu pořád je a to jestli na takouvou s prominutím pííí potřebuju xyz hlavičkovejch souborů a nebo opravdu stačí iostream a string děkuji všem za odpověď a za komentáře
PS mám v sobě pár piv tak sa rozkecávám XD
Eduard Boldižár Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Eduard Boldižár 22. 12. 2016, 22:07:29
Odpovědět  Odkaz 
Na druhej strane uznávam, že sú i iné cesty k reverznému reťazcu. Avšak, vybral som si túto možnosť. Ak užívateľ chce vedieť o problematike viac, určite mú google poslúži. Do článku sa nemôže zmestiť komplexná problematika.
Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
NULL 22. 12. 2016, 23:26:17
Odpovědět  Odkaz 
Pane Boldižár, to že někde někdo něco napíše, ještě neznamená automaticky že je správně znova opakovat ty samé chyby. Jenom jstše se zařadil do řady, na kterou zase pak bude někdo odkazovat, že se to tak pro začátečníky psát může, protože už to tak někdo píše a nevadí to - vadí to. Většinou se pro začátečníky dělají příklady jednoduché, resp. zjednodušené, ne že se kašle na chyby. A odpověď že je to přece jenom pro začátečníky a chyby a bezp. problémy se řeší až na ně dojde je už snad dokonce úplná drzost. To se na mě nezlobte, každý člověk dělá chyby, nikdo neví všechno, ale toto co jste předvedl jako reakci na kritiku, a ještě k tomu plně oprávněnou, tak to mě opravdu šokovalo.
Eduard Boldižár Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Eduard Boldižár 22. 12. 2016, 23:46:59
Odpovědět  Odkaz 
Tak mi ukážte, kde je chyba? Neskompiluje to? Vyhodí to error? Algoritmus je zlý a človek nepochopí, o čo ide (o výmenu písmen v reťazci)? to nie je chyba, len iný postup. A v tom je rozdiel! Ten algoritmus sa učí všade na školách, tam mi nepíšte, že je to chyba. To, že je to zjednodušene, áno, je to zjednodušené. Zabúda na mnoho veci ako vymenoval dotyčný, ale človek sa dopatrá k výsledku, ak samozrejme človek nenapíše do inputu žiadne slovo.
Re: Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Jardík 23. 12. 2016, 00:46:15
Odpovědět  Odkaz 
> cin >> word;
Chyba 1, neošetřený vstup

> int j = word.size() - 1;
Chyba 2, možné přetečení, kvůli tomu špatný výsledek operace. Když na své platformě zadám 2G+1 znaků, výsledek je jen 1 znak.

Článek může být pro začátčníky, ale začátečník pro začátek potřebuje vědět, co je to proměnná, znát základní datové typy, rozdíl mezi znaménkovým a neznaménkovým, znát standardem garantované rozsahy, aby věděl, jaký typ je dobré použít pro danou situaci, vědět, co je to implicitní a explicitní konverze a jak se chová. Až tohle pochopí, tak se může posunout dále. Když mu to ale vysvětlíte špatně a naučíte špatným návykům?
Re: Re: Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Jardík 23. 12. 2016, 00:48:23
Odpovědět  Odkaz 
Nebo spíše 2G+2 znaků
Eduard Boldižár Re: Re: Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Eduard Boldižár 23. 12. 2016, 00:59:07
Odpovědět  Odkaz 
Chyba 1: no lebo všetky tutoriály toto riešia že? Riešenie vstupov a výnimiek je dosť neľahká vec. Tak či tak, ak užívateľ nezadá nič, inak povedané word.size je 0, tak ani nedôjde k vykonaniu algoritmu a následne ani k žiadnej chybe.
Chyba 2: ukázal som vám, že pretečeniu nedôjde. Ani keby bol jeden znak. Dôjde len k tomu, že i bude 0, j bude 0 a nedôjde ani k uskutočneniu cyklu.
Re: Re: Re: Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Jardík 23. 12. 2016, 01:17:27
Odpovědět  Odkaz 
Neukázal jste nic. Pojďme si projít chování programu.

Situace 1)
Program vypíše "Napiste lubovolne slovo: " a čeká na vstup. Já zmáčknu Ctrl+D (konec vstupu na mé platformě). Protože vstup není ošetřen, je word.size() rovno 0. Do proměnné i se přiřadí hodnota 0. Výsledek výrazu word.size()-1 je std::numeric_limits< string::size_type >::max(). Na mé platformě má int 4B, string::size_type 8. std::numeric_limits< int >::max() je na mé platformě 2G, std::numeric_limits< string::size_type >::max() je 8G. Je vidět, že prostě hodnotu 8G do typu int nedostanu. int je znaménkový, při implicitní konverzi dojde k overflow, nedefinovanému chování a hodnota j může být jakákoliv. V "dobrém" případě -1, ale klidně i 2G, protože je to nedefinované chování. Nebo se mi může smazat domovský adresář.

Situace 2)
Program vypíše "Napiste lubovolne slovo: " a čeká na vstup. Já zadám 2G+2 znaků. Můj systém má dostatek paměti. Výsledek výrazu word.size()-1 je 2G-1. Při implicitní konverzi na int pak můžu dostat cokoliv, protože opět došlo k přetečení. V "dobrém případě" to bude 1 a program vypíše pouze jeden znak. Nebo tam bude -3 a program nevypíše nic. A nebo se mi smaže domovský adresář.
Re: Re: Re: Re: Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Jardík 23. 12. 2016, 01:19:40
Odpovědět  Odkaz 
Překlep: Výsledek výrazu word.size()-1 je 2G+1.
Eduard Boldižár Re: Re: Re: Re: Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Eduard Boldižár 23. 12. 2016, 01:34:13
Odpovědět  Odkaz 
Je to zaujímavý problém. Áno, mne to funguje v troch počítačoch a v Linuxe i Windowse. Ale asi som mal šťastie. Ale ako hovoríte, v istej možnej asi malej pravdepodobností to môže vymazať domovský priečinok. O tomto probléme vskútku počujem prvý krát, sa priznám. Pretože mi to stále fungovalo, tak som neriešil a ani nehĺbal odpovede. Dokonca tento príklad je v jednom tutoriali ale samozrejme v trochu inej forme. Ja som ho len upravil pre potreby tohto seriálu. Ale podobne ako ja i oni neriešili tento „hack“ systému. Ok, algoritmus sa upraví tak, aby j nikdy nekleslo na nulu a nižšie. Vďaka za vysvetlenie. Ospravedlňujem sa že som tak vyštekol, ale nevedel som o tomto druhu pruseru.
Eduard Boldižár Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Eduard Boldižár 23. 12. 2016, 00:01:43
Odpovědět  Odkaz 
Ešte sa musím vrátiť k tomu druhému bodu. K vašej zmienenej situácie, že dôjde pri 0 k pretečeniu. No nedôjde, keďže algoritmus nedovolí, aby j kleslo na nulu.
Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Jardík 23. 12. 2016, 00:33:43
Odpovědět  Odkaz 
> int j = word.size() - 1;
A když je word.size() rovno 0?
Eduard Boldižár Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Eduard Boldižár 23. 12. 2016, 00:36:38
Odpovědět  Odkaz 
Tak algoritmus neurobí nič, pretože neplatí i
Eduard Boldižár Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Eduard Boldižár 23. 12. 2016, 00:38:08
Odpovědět  Odkaz 
Ach jo, napodobne mi to kuslo odpoveď. Tak ešte raz:
i bude 0, j bude -1, neplatí podmienka i < j a tak sa cyklus nevykoná!
Re: Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Jardík 23. 12. 2016, 00:56:36
Odpovědět  Odkaz 
To je ale ten problém, j vůbec nemusí být -1. Výsledkem výrazu word.size() - 1 je neznaménková hodnota. Pokud hodnota při přiřazení do 'j' nemůže být reprezentována typem int (velká hodnota), dojde k přetečení a tedy nedefinovanému chování. Toho je využíváno při různých optimalizací cyklů apod. Za to, že tam u vás je -1 a funguje to, jak jste zamýšlel, můžete být rád.
Re: Re: Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Jardík 23. 12. 2016, 01:02:45
Odpovědět  Odkaz 
Jinak mou kritiku neberte jako něco proti vám. Já mám takovéto připomínky pořád na každého. Já jen upozorňuji na problém a budu rád, pokud si ho také uvědomíte a příště se chyby nedopustíte. A pokud ano, tak na něj třeba upozorním znovu, a nebo mě to přestane bavit a neupozorním.
Eduard Boldižár Re: Re: Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Eduard Boldižár 23. 12. 2016, 01:05:34
Odpovědět  Odkaz 
Ja stále používam tento prepis cyklu a ešte ani raz má nesklamal. Na druhej strane som stále len v malých hladinách a tak hlboko do dôsledkov som neskúmal. Nehovorím, že sa to zrubať môže pri nejakých exotických číslach. Sam som sa s tým ešte nestretol. Ale vďaka za poznanie. I keď, pre potreby tohto seriálu budem i naďalej používať. Resp. pre vaše upokojenie, dám podmienku j > 0 v ďalších cykloch, ak sa to vyskytne. Škoda že ste takto nezačali a nemuseli sme tu absolvovať slovnú vojnu.
Programovanie v jazyku C++: Bloky a reverzný reťazec
Jardík 22. 12. 2016, 21:40:24
Odpovědět  Odkaz 
Redakční systém mi ukradl z příspěvku znaky: std::numeric_limits::max< string::size_type >::max()
Eduard Boldižár Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Eduard Boldižár 23. 12. 2016, 00:33:51
Odpovědět  Odkaz 
Inak by ma zaujímalo, prečo som povinný doplniť užívateľovi to, že si dávaj pozor na 0? Algoritmus to v tomto prípade nepotrebuje. A v iných prípadoch sa to ošetrí:
for(i = 0, j = word.size() - 1; i < j && j > 0; i++, j--). Alebo máte na myslí niečo iné, čo mi uniká?
Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Tomáš 23. 12. 2016, 10:01:23
Odpovědět  Odkaz 
Ošetřit nulovou délku vstupního řetězce je nutné z následujících důvodů.

Ve vašem 4. příkladu přiřazujete do proměnné j výsledek volání funkce size(). Funkce size() vrací hodnotu typu size_t.

To lze doložit dokumentací funkce size(): http://www.cplusplus.com/reference/string/string/size/.

Dokumentace datového typu size_t: http://www.cplusplus.com/reference/cstddef/size_t/.

V dokumentaci typu size_t je doslova uvedeno "Alias of one of the fundamental unsigned integer types.", otrocky přeloženo: "Jiný název pro jeden ze základních neznaménkových celočíselných typů".

Jestliže od nulové hodnoty unsigned typu odečtete jedničku, dostanete podle normy: "If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2^n where n is the number of bits used to represent the unsigned type).".

Tuto formulaci bych začátečníkům asi nechtěl vysvětlovat, zjednodušeně to znamená, že když dojde k podtečení, přičítá se maximální možná hodnota daného datového typu tak dlouho, až výsledná hodnota bude v povoleném rozsahu pro tento typ.

V našem konkrétním případě: po podtečení na -1 následuje "korekce" na maximální možnou hodnotu datového typu size_t. Pracovně ji označím DELKA.

Protože DELKA může (ale nemusí) být typu long a typ long může (ale nemusí) mít větší rozsah než typ int, může (ale nemusí) při přiřazení DELKA do proměnné j dojít k přetečení a následné korekci hodnoty.

Pokud se to na konkrétní platformě "hezky sejde", dojde ke korekci na zápornou hodnotu a algoritmus funguje, nikoli však proto, že je dobře napsaný, ale proto, že jsme prostě měli štěstí.

Pokud se to na konkrétní platformě "ošklivě sejde", bude se algoritmus chovat neočekávaně.
Eduard Boldižár Re: Re: Re: Programovanie v jazyku C++: Bloky a reverzný reťazec
Eduard Boldižár 23. 12. 2016, 11:34:09
Odpovědět  Odkaz 
Vďaka za odpoveď. Už je mi problematika jasnejšia.
Eduard Boldižár Programovanie v jazyku C++: Bloky a reverzný reťazec
Eduard Boldižár 23. 12. 2016, 16:08:38
Odpovědět  Odkaz 
Takže bol upravený program. Úprava sa týkala vstupu aj j. Snáď bola diskusia plodná aj pre nováčikov, aby nerobili tú istú chybu ako ja a nielen to, ale i dôvod, prečo je to tak.

Odpovědět

Nejsou podporovány žádné značky, komentáře jsou jen čistě textové. Více o diskuzích a pravidlech najdete v nápovědě.
Diskuzi můžete sledovat pomocí RSS kanálu rss



 
 

Top články z OpenOffice.cz