Kurs programování "C" část 6.

©=37,/=92,}=123,(=91,)=93,"=39,B=38

definice znaku procenta, obráceného lomítka, levé složené závorky, levé a pravé lomené závorky, apostrofu.

a nově znaku and

Před textem vlastního kursu uvedu několik poznámek. Především jeho obsah bude odlišný od pŮvodního záměru. Chtěl jsem připravit řešení velmi zajímavého slovně-matematického hlavolamu, ale narazil jsem na jeho příliš velkou složitost. Nezvládnul jsem program vymyslet a navíc se problém dotýká hranice řešitelnosti úloh na počítači. I když jde o v principu jednoduchý algoritmus, narůstá příšerně doba potřebná pro výpočet. Zatím jsem s jeho odlaďováním přešel na STE na 16 MHz.

Druhým mým problémem je nedostatečné zvládnutí problematiky směrníků a pointerů. (Mám v tom chaos.) Proto je nebudu v této lekci rozebírat nijak do hloubky.

Z veřejného archivu pro ATARI jsem získal nedávno dvě verze vylepšeného kompilátoru pro DEEP BLUE C. Umí lomené závorky a zřejmě i datové struktury. Rozumí si s ním normální linker, i když je v původním manuálu napsáno, že odmítne soubor nachystaný kompilátorem s jiným číslem verze.

Škoda, že paměť vyhrazená pro psaný program je dosti malá. Zkusil jsem zkompilovat krátkou textovou hru od Jirky připravenou původně pro FLOP, když ještě nevěděl, že datové struktury nemůže používat. Nový kompilátor text bez problémů bral, dokud nenahlásil malou paměť.

Přes toto omezení jsem se rozhodl kompilátor zařadit jako dolněk kursu C a nadále používat zdrojové výpisy chystané pro něj. Přiblížíme se tak více stylu psaní programů pro Céčka na větších počítačích a o to tady jde. Mám k němu i delší popis v angličtině, který se mi zdá do magazínu zbytečné cpát. Samozřejmě jej pošlu každému případnému zájemci.

Podmíněný výraz - ternární operátor.

Tato zvláštní věc připomíná pomíněný příkaz IF. Jeho zápis je kratší, ale použít se dá jen pro přiřazení. Jeho základní syntaxe je:

Výsledná hodota výrazu závisý na pravdivosti jeho části uvedené před operátorem otazník. Podle učebnice je užívání podmíněného výrazu málo časté.

výraz_podm ? výraz_1:výraz_2

Příklad převodu znaků na malá písmena s použitím obou způsobů

podmínka  if (c>="A" BB c<="Z")
                      c=c+("a"-"A");

podmíněný výraz

       c= (c>="A" BB c<= "Z")? c+("a"-"A") : c;

Zde se ovšem z podmíněného výrazu stává příkaz, protože je ukončen středníkem.

( inverzní B zde definuje znak "and" pro tiskárnu )

Př.2: Chcete přiřadit proměnné x absolutní hodnotu proměnné y:

dříve
  if (y>=0) x=y; else x=-y;

nově
  x = (y>=0)? y : -y;

Pozorný čtenář si všimne, že první příklad nepřináší žádnou úsporu, protože v něm dochází někdy ke zbytečnému přiřazování. Druhý příklad od kolegy Radka má svůj smysl.

Pointery a adresové operátory I

Používání ukazatelů na adresu a adresových operátorů je to nějděsivější, co jsem zatím na Céčku objevil. Jenže bez nich by nebylo Céčko Céčkem. Přitom jsem nenašel ani jednoznačné názvy obou operátorů, vyjma cizích, to se pak špatně o nich píše.

Základní užití je pro práci s proměnnými a vůbec hodnotami. Za jistých okolností také asi může pointer nahradit známé basicovské příkazy PEEK a POKE. To zatím necháme stranou. Vzpomeňte si, že proměnné jsou lokálního charakteru (většinou), což znamená, že platí pro funkci, v jejímž těle byly deklarovány. To je sice hezké, ale také to na první pohled jednotlivé funkce jaksi izoluje. Sice jsme již používali několik funkcí, které vracely určitou hodnotu, například getchar(), ale často je potřeba funkcím řadu proměnných předávat pro jejich další zpracování.

Situaci řeší ukazatelé na adresy hodnot. Mějme za příklad standartní funkci scanf() pro formátovaný vstup. (V našich knihovnách chybí, proto dosud nebyla v kursu použita.) Její tvar je podobný funkci printf(). Obsahuje také formátovací řetězec s předepsaným tvarem vstupu proměnných a jejich seznam. Situace je ale zásadně odlišná. Zatímco funkci printf() jsou při jejím vyvolání předány hodnoty proměnných pro vytištění, funkce scanf() potřebuje měnit obsah původních proměnných.

Proto se funkci předají pomocí referenčního operátoru B adresy vlastních proměnných.

příklad:

  
  int a,b;
  char string(100);
  scanf("©u©d©s",Ba,Bb,string);
  printf("unsigned - bez znaménka:©u  int: ©d  /n©s",a,b,string);

Formátovací prvek ©u očekává číselnou hodnotu bez znaménka, tedy pouze kladná čísla, ©d číslo znaménkové, ale pouze celé, ©s značí zpracování řetězce.

Výraz Ba předává funkci skutečnou adresu proměnné v paměti, na kterou zapíše vstupující hodnotu. Použití skrývá stejné nebezpečí jako POKE u BASICu. Stačí něco splést a už se vesele přepisují systémové registry a jiná zajímavá místa v paměti.

Aby užití směrníků nebylo příliš jednotvárné, chybí u jména řetězcové proměnné. Je totiž samo o sobě ukazatelem na adresu v paměti, na místo uložení začátku řetězce.

Druhým operátorem, podle učebnice vlastně prvním, je deferenční operátor *, který bude dále nazýván pointer. Je to proměnná, která obsahuje adresu, na níž je hodnota zpracovávané proměnné. Připomíná mi to způsoby adresování používáné ve strojovém jazyce mikroprocesorů. Samotný pointer má také nějaké umístění v paměti, jeho adresa se běžně nevyužívá. Důležitější je vědět, že pointer se deklaruje na stejný datový typ, jako proměnná,na niž má ukazovat.

    int i,*p_i;
     p_i = Bi;

První řádek deklaruje proměnnou s názvem i a pointer na typ integer s názvem p_i. Ve druhém řádku se pointer inicializuje adresou proměnné i, čili od tohoto okamžiku na ni ukazuje. Pro její vlastní změnu je možné použít dále dva způsoby:

    i = 5;
    *p_i = 5;

Oba příkazy udělají totéž. Význam použití pointeru je při zpracování hodnot proměnných ve volaných funkcích. Těm se předávají adresy a v jejich definicích (prostě těle funkce) jsou použity pointery. Ty musí být i ve funkci scanf(), ovšem její zdrojový výpis nemám k dispozici.

Všiměte si, že pomocí pointeru se dá vlastně nahradit příkaz POKE, protože jej můžeme inicializovat libovolnou hodnotou. Tedy místo p_i = Bx se uvede přímá hodnota třeba p_i = 20000. Použití pointerů v tomto smyslu příručka nijak nerozvádí a tak mi zatím není jasné, na jakou fyzickou adresu vlastně ukazuje, protože proměnné běžně zabírají více než jeden bajt.

Jméno pointeru nemusí samozřejmě začínat znaky p_, ale je to tak výhodné pro jejich snadnou identifikaci v textu programu.

Jako příloha ke kursu jsou uvedeny dva výpisy programů opakujících starší látku. První obsahuje dvě řešení výpočtu nejmenšího společného dělitele uvedené v minule recenzované knize "Začínáme s programováním". V ní jsou napsané v PASCALU, ale pro jejich jednoduchost není žádný problém je přepsat do jiného programovacího jazyka.

Hlavní funkce main() nemá s vlastním výpočtem nic společného, slouží jen pro možnost volby opětného výpočtu nebo pro ukončení programu. Je prakticky převzaná z minulého výpisu.

Ve vlastní funkci delitel() jsou předvedeny dva způsoby vstupu odpovědi na otázku. Při použití getchar() se musí uvést funkce ještě jednou pro zlikvidování kódu klávesy Return.

Funkce gets() vyžaduje použití další poměrně dlouhé pomocné proměnné. Ošetří si ale sama Return a také chybně napsaný víceznakový vstup, například celé slůvko "ano"

Druhý příklad je převzaný z příručky "Od Basicu k Céčku", se kterou jsem začínal své první krůčky. Posuzuje pravdivost odpovědí na otázky ohledně stavu počasí podle logických podmínek vzniku duhy.

Zadávání čísel je děláno jako minule pomocí vstupu řetězce a jeho následného převodu na číslo. Naštěstí jsem již pochopil, proč při zadání větší hodnoty vrací funkce jakésy záporné a vůbec podivné hodnoty. Nic podivného na tom není, protože to souvisí s velikostí data typu INTEGER. Jeho běžná délka je uložena ve dvou bajtech, může nabýt tedy nejvyšší hodnoty teoreticky 65535. Jenže zase je normálně chápáno jako číslo se znaménkem, proto je jeho rozsah omezen od -32768 do +32767. Taky mě to mohlo dojít už dřív, protože mezní hodnoty proměnných jsou u popisu datových typů v příručkách uváděny a já to taky četl.

Při psaní obou programů moje hlavní chyba byla v zapomenutí úpravy přebíraných úseků zdrojových textů z jiných programů. Opoměl jsem v nich po přenosu přejmenovat použité proměnné.