C suli 1 - Néhány dolog a C nyelvről, szám ábrázolás

2016-10-27

 

Sok programozási nyelv van. Azért találnak ki manapság is újabbakat, mert egyes feladatokat ezekben könnyebb megoldani. Sok nyelv alapja a C. Az emberi nyelvekhez képest a C csupán néhány kulcsszóból áll, de ezek lefedik a programozásban felmerülő összes vezérlési szerkezetet. C-ben minden programozási feldatot meg lehet oldani. Ez úgy lehetséges, hogy a C nyelv könnyen bővíthető előre elkészített program modulokkal. Írtam már, hogy egész közvetlenül lehetővé teszi az áramkörök, a hardware programozását. A C-nél gyorsabb és közvetlenebb vezérlést tesz lehetővé az Assembly nyelv. A C-vel ellentétben, az Assemly nyelv processzor családonként eltérő, vagyis gépfüggő. Ha valami űber gyors dolgot kell alkotnunk, azt is megtehetjük, hogy a C programunkba közvetlenül Assembly rutint teszünk.

Nézzük, ezt a néhány varázsigét, amelyekkel megmondjuk, hogy mit kell csinálni. Ezek közül is csak a vastag betűseket fogjuk használni:
goto, return, if, else, for, break, continue, do, while, switch, case, default, sizeof, extern, void

Kelleni fognak még az alábbi kulcsszavak is, ezekkel az adatok típusát fogjuk megadni:
unsigned, int, char, short, long, double, float, union, register, static, typedef, struct, volatile

Manapság mindenkinek ismerős a digitális számítógép kifejezés, naponta röpködnek a bit-ek, a gígabyte-ok, és a megapixelek :). De biztos van, akinek a kedvéért érdemes ezektől a dolgoktól kezdenünk. Mielőtt az elektromosságot felfedezték, mechanikus automatákat, számoló masinákat készítettek. Az elektromosság, pontosabban az elektronika fejlődésével azután kitárultak a lehetőségek. Kezdetben voltak analóg számítógépek is, de sokkal megbízhatóbbaknak bizonyultak a digitálisok, melyek áramköreinek két állapotuk van. Egy kapcsolónál ez jelenti, hogy be, vagy ki van kapcsolva, egy lámpánál, hogy az világít, vagy sem. Elektromérnökök megörültek, ilyen áramkörök olcsóbbak és egyszerűbbek. Matematikusok megörültek, mert az elméletben már korábban kigondolt kettes számrendszer végre a gyakorlatban is hasznossá vált. Amikor az elektronikában azt látjuk, hogy egy lámpa ég, vagy nem, az matematikus 1-t, vagy 0-t ír, számítástechnikában meg 1 bit adatról beszélünk. A bit a binary digit kifejezésből jön (lehet, hogy görögből :), egy kettes számrendszerbeli számjegyet jelent. A benzinkutasok már a tizes számrendszerbeli digit (számjegy) miatt is nagyon szomorúak voltak, hogy csupán 0..9 lehet a benzin ára, ezért kitalálták, hogy több számjegyet írnak egymás mellé. A benzinkúton jobb szélen vannak az egyesek, tőle balra a tizesek, majd a százasok... A kettes számrendszerben is így néznek ki a számok. Jobb szélen vannak az 1-esek, majd a 2-esek, a 4-esek :)... Amíg fejben egy szép nagy számot is el tudunk képzelni, mondjuk amikor bedobunk egy lottó szelvényt, addig szerencsére a benzin kutak nem tudnak egy tetszőlegesen nagy számot kiírni. A mi uC-nk alap esetben 8 számjeggyel, 8 bit-tel, más néven 1 byte-tal tud egyszerre dolgozni. Szükség esetén nagyobb számokkal, matematikai függvényekkel is megbírkózik, külön program futtatásával. Leggyakrabban ilyen 1 byte-os adatokkal fogunk dolgozni, amit C-ben char -nak (character, betü) nevezünk. C-ben alapból a változók signed, azaz előjelesek. Előb nézzük az unsigned char azaz előjel nélküli char bit-jeit. Tizes számrendszernek megfelelően 0..255 közötti számok tárolására alkalmas.

Ez az ábra mutatja char-t, a signed-t C-ben nem írjuk ki. A legnagyobb helyiértékű bit mutatja az előjelelőjelet. Ha az értéke 1, akkor az eltárolt szám negatív. Ebben -127..127 közötti számokat tudunk tárolni.

Gyakran használjuk a számok hexadecimális, vagyis 16-os számrendszerbeli alakját. Itt az látható, hogy a byte-ot kétfelé vágva, két hexadecimális számjegyet kapunk.

A C 2-es, 8-as, 10-es és 16-os számrendszerben képes tőlünk számokat elfogadni. Nézzük, meg hogyan néz ki az első 16 szám? A C arról fogja felismerni a bináris számot, hogy "0B"-vel kezdődik, az oktális "0"-val, a hexadecimális "0x"-szel, a tizesest meg úgy írjuk, mint normálisan. A számítógépek szigorúan megkülönböztetik a "0" - nulla számjegyet, a nagy "O" betütől, és a kis "o" betütől, számokban kizárólag a "0" számjegyet használhatjuk.
(Ilyen sárga alapon lesznek majd a program részleteket is.)


tizes      kettes      nyolcas  tizenhatos
decimális  bináris     oktális  hexadecimális

 0         0B00000000     0     0x00
 1         0B00000001    01     0x01
 2         0B00000010    02     0x02
 3         0B00000011    03     0x03
 4         0B00000100    04     0x04
 5         0B00000101    05     0x05
 6         0B00000110    06     0x06
 7         0B00000111    07     0x07
 8         0B00001000   010     0x08
 9         0B00001001   011     0x09
10         0B00001010   012     0x0A
11         0B00001011   013     0x0B
12         0B00001100   014     0x0C
13         0B00001101   015     0x0D
14         0B00001110   016     0x0E
15         0B00001111   017     0x0F

Matematikában az ismeretlen mennyiségeket betükkel jelöljük, például "a+b=c". Programozásban ezeket változóknak nevezzük. C-ben nem kell egyetlen betüre szorítkoznunk, hanem mindenféle nevekkel illethetjük a változóinkat, néhány korlátozással. Nem használhatjuk például a fentebb már megismert kulcsszavakat. Azután egy változó név nem kezdődhet számjeggyel, és az "_" - aláhúzás jelen kívűl nem tartalmazhat írásjeleket sem. A C megkülönbözteti a kis és nagy betüket. Tehát ha például azt írjuk, hogy "kapcsolo", "Kapcsolo", vagy "KAPCSOLO", a C meg lesz győződve arról, hogy három különböző dologról van szó. Na, akkor legyen egy "kapcsolo"-nk.


char kapcsolo;

Ez azt jelenti, hogy lefoglatunk egy char típusú, kapcsolo nevű változóhoz szükséges helyet a memóriában, a továbbiakban a programukban tetszőlegesen kiolvashatjuk az értékét, vagy beírhatunk egy új értéket. A sor végét C-ben "; " - pontosveszzővel kell lezárni.


kapcsolo = 1;

Az "=" - egyenlőség jel a C-ben értékadást jelent. Tehát a kapcsolo értéke legyen 1, vagyis a kapcsolónak lefoglalt memóriába eltárolja az új értéket.


char kapcsolo = 1;

C-ben nem kötelező a vátozónak rögtön a létrehozásakor értéket is adni, de megtehetjük, és szerintem célszerű.


char kapcsolo = 1;
char led = 0;
if ( kapcsolo == 1) led = 1;

Elég bugyuta példa, de nézzük. Van egy kapcsolónk, meg egy ledünk. A LED-ről majd később lesz még szó, egy világító dióda, lámpa helyett használjuk. A kapcsolót hadd ne magyarázzam el. A kapcsoló 1-be állítjuk. Az if utasítás megvizsgálja a "()" közötti feltételt, és ha az igaz, végrehajtja az utána következő utasítást, vagyis a ledbe 1-et ír. Az "==" nem elírás, a C-ben ez az egyenlőség feltétel jele. Milyen feltételeket alkalmazhatunk még? Használhatjuk a "<", ">", "<=", ">=", és a "!=". A legutolsó azt jelenti, hogy nem egyenlő. Persze a C nem fog megijedni a "<>" relációtól sem, de "!=" szerintem kifejezőbb. A következő példában akkor állítanánk 1-be a ledet, ha a kapcsoló értéke 0 lenne.


char kapcsolo = 1;
char led1 = 0;
char led2 = 0;
if ( kapcsolo == 1)
{
   led1 = 1;
   led2 = 0;
}

A "{ }" - körmös zárójelekkel tudunk több utasítást összekapcsolni egy blokkba, amit a C úgy kezel, mintha egyetlen utasítás lenne. Tehát mivel a feltétel teljesül, led1 értéke 1, led2 értéke 0 lesz.


char kapcsolo = 1;
char led1 = 0;
char led2 = 0;
if ( kapcsolo == 1)
{
   led1 = 1;
   led2 = 0;
}
else
{
   led1 = 0;
   led2 = 1;
}

Az if utasításnak lehet else ága is. Az if utáni parancs a feltétel teljesülésekor hajtódik végre, az else utáni parancs pedig akkor, ha a feltétel nem teljesül. Tehát amikor a kapcsolo 1-ben van, led1-et 1-be, led2-t 0-ba állítjuk. Amikor pedig a kapcsolo 0-ban van, led1-et 0-ba, led2-t 1-be állítjuk.


char kapcsolo = 1;
char led1 = 0;
char led2 = 0;
if ( kapcsolo)
{
   led1 = 1;
   led2 = 0;
}
else
{
   led1 = 0;
   led2 = 1;
}

A C-ben a 0 érték felel meg Hamisnak (False), és minden egyéb Igaznak (True). Ezért nem kell kiírnunk a feltételt. A C meg fogja vizsgálni, hogy a kapcsolo értéke 0, vagy nem, így az alábbi program működése meg fog egyezni az előzővel.


char i = 10;
char led = 0;
while( i)
{
  i = i - 1;
  led = 1;
  led = 0;
}

A while utasítás külalakra nagyon hasonlít az if-hez. Bizony ő is megvizsgálja a "( )" közötti feltételt, de az if-fel ellentétben nem egyszer hajtja végre az utasítást, henem mindaddig ciklikusan újra és újra végrahajtja, amíg a feltétel igaz. A példában van egy i vátozónk, aminek 10-es kezdőértéket adtunk. A "while" megvizsgálja i-t, és ha nem 0 az értéke, végrehajtja az utasítás csoportot. Abban pedig az van, hogy i-nek az értéke legyen i-1, vagyis i értékét csökkentse 1-gyel. Azután led legyen 1, majd led legyen 0. A 2. körben i-nek már csak 9 lesz az értéke.. a 11. körben i értéke 0 lesz. Ekkor a 'while" utáni utasítás nem kerül végrehajtásra, hanem a program tovább lép.


char i = 10;
char led = 0;
while( i)
{
  i-= 1;
  led = 1;
  led = 0;
}

Lustuljunk! Hogyan lehetne ezt egyszerűbben írni? Ezt a forma ugyan azt jelenti mint az előző, i értékéből kivon 1-et. A többi műveletet még nem néztük meg, de az összeadásnak, szorzásnak, osztásnak is van hasonló alakja.


char i = 10;
char led = 0;
while( i)
{
  i--;
  led = 1;
  led = 0;
}

Lehet ezt tovább fokozni? Igen! Egy változó értékének a csökkentése (decrementing --) vagy növelése (incrementing ++) annyira gyakori művelet, hogy a mikroprocesszoroknak külön utasításuk is van rá. A hozzám hasonló lusta programozók kedvéért, a C-ben is van erre külön utasítás.


char i = 10;
char led = 0;
while( i--)
{
  led = 1;
  led = 0;
}

Fokozzunk! Miért kell ezt külön sorba írni? Ez a program teljesen egyező eredménnyel fut mint az előző. A while megvizsgálja i értékét, majd csökkenti 1-gyel.


char i = 10;
char led = 0;
while( --i)
{
  led = 1;
  led = 0;
}

Írhatjuk ezt is, de ez mást jelent. Az fog történni, hogy i értékét előbb csökkenti a program, majd azután fogja az i-t a while megvizsgálni. Vagyis a while már a 10. kör elején azt fogja találni, hogy i értéke 0, és így a ciklusunk csak 9x fog lefutni. Ez nmost hiba, de lehet, hogy valahol erre lesz szükségünk.


char i;
char led = 0;
for( i = 10; i != 0; i--)
{
  led = 1;
  led = 0;
}

A while-hoz hasonló utasítás a for, használhatjuk, ha alkalmasabbnak találjuk. A for-nak a "( )" - zárójelek között három tagot kell megadni ";" - pontosvesszővel elválasztva. Az első tag a for első futásakor hajtódik csak végre, itt adunk i-nek kezdőértéket. A második tag a ciklusok végrahajtás elején megvizsgálandó feltétel, megnézzük, hogy i != 0. A harmadk tag ciklusonként lefut, itt csökkentjük i értékét.


char led;
for( ; ; )
{
  led = 1;
  led = 0;
}

Olyan is előfordul, hogy végtelen ciklusra van szükségünk, vagyis az adott program rész folyamatosan újra és újra lefusson. Így néz ki a led értékének folyamatos változtatása.