Bevezető Ez cikksorozat elsősorban azoknak készül, akik nem győzik elolvasni a kötelező szakirodalmat, akik tűkön ülve várják, hogy elkezdhessék megírni az első Hello world! programjukat Ez tulajdonképpen nem baj, mert később eldönthetik, hogy akarják-e folytatni, avagy nem Ha ez előbbi úton indulnak el, akkor maguktól rádöbbennek, hogy igenis szükséges az a szakirodalom, s majd önállóan kezdenek el kutakodni utána A C programozási nyelv platformfüggetlen használható Linux, Windows, Macintosh és egyéb operációs rendszereken át az Intel, Atmel, Microchip mikrokontrollereken keresztül mindenhol, ahol program fut Az én cikkem az operációs rendszereken használt C programról, azon belül is Linuxon futó programok írásáról szól Példák az egyéb helyeken használható fordítókra: Fordító neve Környezet Programozási nyelv Dev-C++ Microsoft Windows, Linux C, C++ nyíltforrású Lcc-win32 Microsoft Windows C nyíltforrású Keil C51 Mikrokontroller C (Intel 8051) shareware HI-Tech Mikrokontroller C (PIC) shareware Microchip MCCxx Mikrokontroller C (PIC) shareware WinAVR Mikrokontroller C (AVR) freeware Code VisionAVR Mikrokontroller C (AVR) shareware Licensz Természetesen rengeteg sok más egyéb fordító létezik, mindenféle felhasználásra Tehát mint említettem mi a Linuxon futó C fordítóval (gcc) fogunk megismerkedni, de elsősorban a magával a programozási nyelvvel, illetve annak struktúrájával 1
Szia Világ! 10 Elsősorban meg kell ismerkedni néhány alapfogalommal: Allomány: A programunkhoz tartozó minden egyes forrásfájlt állománynak nevezünk, ezek *c vagy *h (header állomány) fájlok A headerben - mint a neve is mutatja (fejléc) -, előre definiálhatunk függvényeket, makrókat, előfordítói feladatokat, stb Változó: A program futása során megváltozható értékeket változókban tároljuk Típusai lehetnek: char karakteres int egész lebegőpontos tört Függvény: Bemeneti és visszatérési értékkel rendelkező összetartozó utasításcsoport Legfontosabb és egyben elengedhetetlen függvény a main() függvény Minden programnak rendelkeznie kell main() függvénnyel Operátor: Értékek közötti művelet, ill utasítás Íme egy példaprogram: #include <stdioh> // stdioh header-t is fordítjuk void main(void) // a főprogram kezdete // ez a karakter nyitja meg a programtörzset int a, b, c; // a, b, c egész típusú változók deklarációja a = 5; // a értéke mostantól 5 b = 10; // b értéke 10 c = a + b; // c változóba a kettő összege kerül printf("a c változó értéke: %i\n",c); // kiíratjuk a képernyőre // lezárjuk függvényt A példaprogramot megtalálod a gyűjteményben (src: helloc) A példaprogramokat le lehet tölteni tömörítve itt Ha letöltötted, nyisd meg a terminált, futtasd a peldaksh (navigálj a peldak mappába, majd /peldaksh) nevű scriptet Ha egy példaprogram működését szeretnéd megnézni, akkor ezentúl mindig ezt a műveletet kell megtenned Ha Microsoft terméket futtatsz gépeden, akkor itt tudod letölteni a példaprogramokat A tömörített mappában megtalálod a *c forrásfájlokat (amiket a Dev- C++ programmal fordítottam) és a *exe futtatható fájlokat Az összefoglalót a peldakexe tartalmazza 2
Operátorok és használatuk Az előző példában már használtunk operátorokat, például az egyenlőségjel vagy az összeadás Nézzük meg, hogy valójában milyen operátorok léteznek és mire is jók egyáltalán Leggyakrabban használt operátorok az aritmetikai- és logikai operátorok, valamint az elválasztást végző operátorok: Operátor Operátor neve Kiértékelés menete Operátor típusa + összeadás balról jobbra aritmetikai - kivonás balról jobbra aritmetikai * szorzás balról jobbra aritmetikai / osztás balról jobbra aritmetikai % maradékképzés balról jobbra aritmetikai = értékadás balról jobbra aritmetikai - egyoperadusú mínusz jobbról balra aritmetikai && logikai ÉS balról jobbra logikai logikai VAGY balról jobbra logikai! logikai NEM jobbról balra logikai A maradékképző (modulo) operátor egy kicsit érdekes Működését példán mutatnám be: void main( void ) int i = 10; // i változót deklaráltuk és 10 egész szám értéket kapott int j = 5; // j változó 5 egész értéket kapott /* egyszerűbben így nézne ki: int i=10,j=5; */ int k; // k deklarálva int l; // l deklarálva k = i/j; l = i%j; l = 7%j; // k értéke 2 lesz // l értéke 0 lesz // l értéke 2 lesz 10 Első esetben k = = 2 ez teljesen világos, de 10 osztva 5-el miért 0? A modulo az 5 osztás eredményét figyelmen kívül hagyja, csak az érdekli, hogy mennyi a maradék Így már világos, ha a 7-et elosztjuk 5-el, megvan 1-szer (ez minket nem érdekel), maradt a 2 A modulo-nak nagy szerepe van például páros számok keresésénél pl: i = x%2; ha i értéke 0, akkor x páros, ha nem nulla, akkor értelemszerűen páratlan 3
Kamaszodunk A példákat végignézve gyorsan rájövünk, hogy a programokat nem azért fogjuk írni, hogy a különböző kiíratásokban gyönyörködjünk, amit két szám egymással való osztásából nyertünk Sokkal komplexebb feladatokat fogunk megoldani A kecskeméti Kandó Kálmán Szakközépiskola átlagon felüli mindennapjaiba belefér, szegény megkeseredett lelkületű diákok életét nyomorgassák a replusz művelettel Feladatunk ezért a Kandósokon okulva, egy replusz program megírása lesz: két szám szorzatát el kell osztanunk ugyanazon két szám összegével, ha lehetséges, ezeket a számokat tizedes tört formában adjuk meg #include <stdioh> void main( void ) R1=10, R2=12, RE; RE = (R1*R2)/(R1+R2); printf("az eredő ellenállás értéke: %f\n",re); A példaprogramot megtalálod a gyűjteményben (src: replusz1c) De valahogy ez mégsem tökéletes, mert így is megtudom mondani az RE értékét (54545), hiszen van a változóknak kezdőértékük A következő példában a program meg is kérdezi az ellenállások értékét, majd azokkal számol, amit megadunk neki, sőt még kulturáltan köszön is! :) #include <stdioh> void main( void ) R1, R2, RE; printf("\nüdvözlöm kedves lelkes!\n\n"); printf("kérem adja meg R1 értékét ohm-ban: "); scanf("%f", &R1); printf("\nkérem adja meg R2 értékét ohm-ban: "); scanf("%f", &R2); RE = (R1*R2)/(R1+R2); printf("az eredő ellenállás értéke: %f ohm (párhuzamos kapcs esetén)\n",re); A példaprogramot megtalálod a gyűjteményben (src: replusz2c) 4
Még mindig a matematika Sok feladat elvégzéséhez szükségünk lesz a matematikában már megszokott és alkalmazott képletekre is Ha egy szám négyzetére vagyunk kíváncsiak akkor megszorozzuk önmagával, ez triviális De mi van akkor, ha éppen 16-dik hatványát szeretnénk megtudni? Elég hosszadalmas lenne begépelgetni, ugyanakkor annak a rizikó faktora is megnő, hogy elrontjuk Vagy esetleg egy szög szinuszára, koszinuszára vagyunk kíváncsiak Ilyen esetekben bizony be kell vonnunk még egy header állományt a játékba, mégpedig a mathh-t A következő példában egy kör adatait fogjuk kiszámolni: #include #include <stdioh> <mathh> void main( void ) r,d,k,t; printf("\nkérem adja meg a kör kerületét: "); scanf("%f", &r); d = 2 * r; K = d * M_PI; T = ( pow(r,2) * M_PI ); // Átmérő kiszámítása // Kerület, M_PI a mathh-ban van definiálva // pow a hatványozás printf("\na kör adatai: \n\n"); printf("átmérő: %f egység\nkerület: %f egység\nterület: %f egység\n\n",d,k,t); A példaprogramot megtalálod a gyűjteményben (src: korc) A mathh-ból elérhető legfontosabb konstansok: Hivatkozási név M_E M_LOG10E M_PI M_PI_2 M_1_PI M_SQRT2 M_SQRT1_2 Konstans értéke e lge π π 2 1 π 2 1 2 5
A mathh-ból elérhető legfontosabb függvények: Visszatérési érték Függvény Matematikai jelentés acos() acosf() asin() asinf() atan() atanf() cos() cosf() pow(, ) powf(, ) sin() sinf() sqrt() sqrtf() tan() tanf() arcus cosinus arcus sinus arcus tangens cosinus hatványozás sinus gyökvonás tangens 6
Körbe-körbe Az alapok megvannak, most ismerkedjünk meg néhány illetve az összes ciklusszervező utasítással, hiszen ciklusok nélkül nagyon bonyolult lenne egy valamire való program Tételezzük fel, hogy egy értéket 25-ször kellene kiíratni a képernyőre, ciklus használata nélkül mind a 25-ször be kellene gépelni a printf() függvényt és argumentumait, rémes lenne A ciklus végrehajtása mindig egy feltételtől függ, tehát egy ciklus mindig magában foglal egy feltételvizsgálatot is Attól függően, hogy ez hol történik meg, léteznek elöltesztelő illetve hátultesztelő ciklusok Az első, amivel meg fogunk ismerkedni, az a for ciklus Ennek az utasításnak minimum 3 kifejezést kell megadnunk példa szerint: for( kifejezés1; kifejezés2; kifejezés3 ) // a ciklus feje // ciklustörzs A feltételvizsgálat a ciklus fejében történik, tehát ez elöltesztelő A ciklus elindulása pillanatában az 1 kifejezés teljesül (csak egyszer hajtódik végre), majd megvizsgálja a 2 kifejezést, ami egy feltétel Ha nem bizonyul igaznak, akkor lép a 3 kifejezésre, ha teljesül, akkor kilép a ciklusból Ezt a folyamatot egy ábrával demonstrálnám: 7
A következő, amivel meg fogunk ismerkedni az a while ciklus: while( kifejezes ) // a ciklus feje // ciklustörzs Amíg a feltétel (kifejezés) igaznak bizonyul, addig hajtja végre a ciklus törzsében leírtakat Ha a kifezes nem igaz, akkor be sem lép A while ciklus is hátultesztelő While folyamatábrája: 8
És végül, de nem utolsó sorban a do-while ciklus: do while( kifejezés ) Ebben az esetben a ciklustörzs egyszer mindenképpen lefut, majd megvizsgálja a kifejezés értékét és ha az igaz, akkor még egyszer lefut Ez addig fog ismétlődni, amíg a kifejezés hamis nem lesz A do-while szerkezet egy hátultesztelő ciklus Do-while folyamatábrája: 9
A ciklusok futását befolyásoló parancsok: break: Hatására a program kilép az aktuális ciklusból Egymásba ágyazott ciklusok esetén a break után eggyel feljebb lévő ciklusba kerül a program continue: Hatására a ciklus további futása helyett a ciklus feltétele kerül újbóli kiértékelésre, tehát a ciklus az elejére ugrik, for ciklus esetén végrehajtódik a 3 kifejezés Mint az az ábrákból kiderülhetett ezeket az utasításokat önmagában értelmetlen lenne használni Éppen ezért fogunk a következőkben megismerkedni a feltételvizsgálatokkal Végtelen ciklusokat is képezhetünk, ilyenkor a ciklustörzs végtelenségig fut, ezt a következőképpen tehetjük meg: for(;;) while(1) // ciklustörzs // ciklustörzs Ne feledd, ami nem 0, az a C-ben igaznak minősül, tehát a -12 is igaz, mert nem nulla! 10
Feltétlenül szükséges feltételek Ha már ennyi mindent tudunk változót deklarálni, matematikai műveleteket végezni, ciklusokat szervezni, akkor tanuljuk határt szabni Ezzel a művelettel programunkat el tudjuk ágaztatni különböző feltételek mellett Például egy megadott számig elszámolva szeretnénk megtudni, hogy hány darab páros szám van Először is egy ciklust kell szerveznünk, amely 1-től elszámol az adott számig A ciklus törzsében el kell helyeznünk egy modulo osztást, az éppen aktuális számot 2-vel, ha a maradék 0, akkor páros, ilyenkor egy számlálót léptetünk, majd a végén kiíratjuk a számláló értékét Az eddigi tudásunk szerint a szám modulo 2 -ig jutnánk el, hiszen itt egy feltételvizsgálatot kell végezni Az if vagy if-else szerkezet elvégzi nekünk a vizsgálatot és elágaztatja a programot Az if létezhet else nélkül, de fordítva nem if( feltétel ) // A feltétel teljesülése esetén if( feltétel ) // Ha a feltétel igaz else // Ha a feltétel hamis VAGY 11
Most már meg tudjuk írni programocskánkat: #include <stdioh> int main() int i, n, modulo, szamlalo = 0; printf("\n\nkérem adjon meg egy egész számot: "); scanf( "%i", &n ); for( i = 1; i <= n; i++ ) modulo = i%2; // A ciklus 1-től n-ig egyesével léptetve tart // A 2-vel való osztás maradéka if( modulo == 0 ) szamlalo++; // Ha ez nulla, // A számláló értéke eggyel nő printf("\n\n1-től %i-ig %i db páros számot találtam\n\n", n, szamlalo); A programot futtatva látjuk (remélem), hogy elég lett volna 2-vel elosztani a megadott számot és máris ugyanazt az eredményt kapjuk De mi van akkor, ha n -ig keressük azokat a négyzetszámokat, amelyek oszthatóak 2-vel, de 5-el már nem Ilyenkor teljesen más a leányzó fekvése, ehhez a feladathoz megéri programot írni Íme a példaprogram: 12
#include <stdioh> #include <mathh> // Használjuk a mathh-t int main() int i, n, negyzet, szamlalo = 0; // Azért van két sorban, int modulo2, modulo5; // hogy kiférjek :) printf("\n\nkérem adjon meg egy egész számot: "); scanf( "%i", &n ); for( i = 1; i <= n; i++ ) negyzet = pow( i,2 ); // A ciklus 1-től n-ig egyesével léptetve tart // Négyzetre emelés if( negyzet > n ) break; else // Ha a négyzet nagyobb, mint a "n", // kilép a ciklusból modulo2 = negyzet%2; // A 2-vel való osztás maradéka modulo5 = negyzet%5; // Az 5-el való osztás maradéka if( modulo2 == 0 && modulo5!= 0 ) // Ha modulo2 nulla // ÉS modulo5 nem nulla, szamlalo++; // A számláló értéke eggyel nő printf("\n\n1-től %i-ig %i db páros, 5-el NEM osztható négyzetszámot találtam\n\n", n, szamlalo); A példaprogramot megtalálod a gyűjteményben (src: matekc) Ezt a programot nem taglalom, mert elég egyértelműnek tűnik 13
Az előző példa egy kicsit tömörebb formában: #include <stdioh> int main() int i, n, szamlalo = 0; printf("\n\nkérem adjon meg egy egész számot: "); scanf( "%i", &n ); for( i = 1; i <= n; i++ ) // A ciklus 1-től n-ig egyesével léptetve tart if( (i*i) > n ) // Négyzetre emelés, vizsgálat, kilépés break; if( 1^((i*i)%2) && 0^((i*i)%5) ) // Oszthatóság vizsgálata szamlalo++; // számláló léptetése printf("\n\n1-től %i-ig %i db páros, 5-el NEM osztható négyzetszámot találtam\n\n", n, szamlalo); Rövidebb, de mit is csinál valójában? A mathh header állomány eltűnt, a ciklus maradt, kevesebb változó lett Magában a feltételvizsgálatban végeztünk egy egyszerű szorzást, ami a szám négyzetét adja, ha ez nagyobb mint n, akkor kilép a ciklusból Láthatóan elhagytam a kapcsos zárójeleket az if-nél, ez megengedhető abban az esetben, ha csak EGYETLEN utasítást kell végrehajtani, ha feltétel igaznak bizonyul Ha a ciklus folytatódik akkor jön a következő vizsgálat, amely hét lépést tartalmaz egyben: a) szorzok négyzet b) modulo 2-vel páros-e a négyzet? Ha igen akkor 1, ha nem akkor 0 c) kizáró VAGY kapcsolat (exlusive OR, XOR) 1-el d) szorzok négyzet e) modulo 5-el osztható-e 5-el a négyzet? Ha igen, akkor 1, ha nem akkor 0 f) XOR 0-val g) a két értéket logkai ÉS kapcsolatba helyezem egymással Az XOR (ejtsd: ikszor) művelet eredménye akkor ad logikai 1 értéket, ha a két különbözik: A B XOR A,B 0 0 0 0 1 1 1 0 1 1 1 0 Lehet agyalni, hogy hogyan működik a program A magyarázat nem férne ki 1 oldalon, ezért nincs 14