Ugrás a tartalomhoz

Bevezetés az Informatikába

Dr. Nyakóné dr. Juhász Katalin, Dr. Terdik György, Biró Piroska, Dr. Kátai Zoltán (2011)

Kempelen Farkas Hallgatói Információs Központ

A programozás alapjai

A programozás alapjai

A számítógép számára a feladat meghatározását programozásnak nevezzük. A programozás valamilyen a programozási nyelv segítségével történik. A programozási nyelv a számítástechnikában használt olyan, az ember által is olvasható és értelmezhető utasítások sorozata, amivel közvetlenül, vagy közvetve (például gépi kódra fordítás után) közölhetjük a számítógéppel egy adott feladat elvégzésének módját.

A programozás egy összetett folyamat, melynek célja és eredménye a programok előállítása. A programozás lépései:

  • a megoldandó feladat megfogalmazása, definiálása;

  • a megfelelő algoritmus kiválasztása, megtervezése;

  • az algoritmus logikai szerkezetének megtervezése;

  • az algoritmus szerkezeti lebontása;

  • az algoritmus kódolása;

  • a kész program futtatása, tesztelése;

  • a dokumentáció elkészítése;

  • a program karbantartása, felügyelete, nyomon követése.

A programozási nyelveket történeti fejlődésük alapján sokféleképpen lehet osztályozni, csoportosítani. A programnyelvek változásának egy-egy fontosabb állomását a programozási nyelvek generációjának nevezzük.

A programozási nyelvek első generációja, a legalacsonyabb szintű programnyelv, a gépi (bináris) kód. A gépi kódot tulajdonképpen nem is lehet igazi programnyelvnek tekinteni, hiszen erre a klasszikus értelemben vett programnyelvi elemek (utasítások, vezérlőszerkezetek, kifejezések) nem jellemzőek.

A számítógéppel való kapcsolat megteremtésének a legközvetlenebb módja viszont a gépi nyelv használata. A gépi nyelvek a számítógépek speciális műszaki tulajdonságait használják ki. Ez az a nyelv, amelyet a processzor közvetlenül megért. A gépi kódú programok az adott számítógép típusokhoz kötődnek, azok más felépítésű számítógépen nem futtathatók. A gépi utasításkészlet bináris számokból álló egyszerű utasítások csoportja, az utasítások pedig műveleti kódból és címrészből tevődnek össze.

A gépi kódban való programozás azonban rendkívül nehézkes, hisz az egyes műveletek kódjának és az utasítások szerkezetének az ismerete is szükséges hozzá. Ezért a gépi kódú programozást főként a számítógépek kezdeti időszakában használták (más lehetőség ekkor még nem is állt rendelkezésre).

A gépi kódú programozás kényelmetlenségeit kiküszöbölendő, az egyes utasításoknak néhány karakteres szimbólumokat, úgynevezett mnemonikokat feleltetnek meg, melyeket programmal gépi kódra fordítanak. A programnyelvek második generációját képviselik az ilyen alacsony szintű (gépközeli) nyelvek. Ezen nyelveknél megmarad a számítógép erőforrásainak (hardver, operációs rendszer) maradéktalan kihasználhatósága, a memóriacímek, a regiszterek, a verem és a megszakítások közvetlen elérhetősége, programozhatósága.

Ilyen második generációs nyelv az assembly, melynek fordítóprogramja az assembler. Az alacsony szintű nyelveknél minden gépi utasításnak megfelel egy nyelvi utasítás (egy nyelvi utasítás azonban állhat több gépi utasításból is). Megjelennek az adatok és az adatok helyfoglalását, a programkód memóriába helyezését definiáló utasítások. A memóriacímek azonosítóval való ellátására is van lehetőség. Elnevezhetjük az adatok kezdőcímét, ebből alakul ki később a változó fogalma, az utasítások címeinek elnevezésből pedig a címke fogalma.

Egy minden szempontból megfelelő assembly szintű program létrehozásához nem csak a processzor végrehajtható utasításait helyettesítő szimbólumok, hanem a program szerkesztését, javítását és dokumentálását megkönnyítő lehetőségek, úgynevezett direktívák is szükségesek. A direktívák csak az assembler számára hordoznak információt.

Azonban még ez a programozási mód is túl nehézkes, és nagyobb feladatok megoldására alkalmatlannak bizonyult. Ezért fejlesztették ki a magas szintű programozási nyelveket, melyek segítségével a megoldandó feladatot könnyebben és tömörebben lehet megfogalmazni. A programnyelvek harmadik generációját nevezzük magas szintű programozási nyelveknek, amelyeket gyakran a 3GL jelöléssel szokás ellátni (3rd Generation Language). A programnyelvek e generációjánál a változófogalom pontosabb megjelenése az egyik legfontosabb tulajdonság. A változó azonosítója nem memória-kezdőcímet, hanem egy egész memóriatartományt jelöl. A változófogalom mellett kialakul a típusfogalom, és lehetőség van arra, hogy a programozó saját típusokat is létrehozzon. Kialakulnak a strukturált programnyelvek, megjelennek az elágazások, a ciklusok, az eljárások és a függvények. Az eljárásoknak lehetnek név, illetve cím szerint átadott paraméterei is. A harmadik generációs programnyelveket feladatorientált vagy eljárásorientált nyelveknek is nevezzük, mivel felépítésükből fakadóan a programfejlesztő számára egy program adott részfeladatainak megoldására szolgáló struktúrákat (eljárásokat) kínálnak. Nagy előnye a magas szintű programnyelveknek, hogy a velük készült programok függetlenek a konkrét számítógéptől, hiszen az egyes gépek specialitásait a fordítóprogramnak kell figyelembe vennie a fordítás során.

Egy-egy magas szintű programnyelvhez minden géptípusra más-más fordítóprogram tartozik. A fordítóprogram (compiler) segítségével lehet a magas szintű programnyelv utasításait az adott számítógép gépi kódú utasításaira lefordítani. Az interaktív gépek megjelenésével kialakult a programozási nyelvek egy olyan csoportja, melyeknél a fordítási menet nem különül el a végrehajtástól, hanem a forrásprogram utasításait rögtön értelmezi és végre is hajtja a fordítóprogram. Az ilyen típusú fordítóprogram az értelmező (interpreter).

Ma már nagyon sok különböző nyelv létezik. Ezek kifejlesztése általában valamilyen típusú problémák, feladatok megoldására irányul - célorientált, problémaorientált. Az egyes programozási nyelvek önálló utasításkészlettel, szabályrendszerekkel rendelkeznek. Ezeket a szerkezeti, formai követelményeket a nyelv szintaxisának nevezzük. Az utasítások szerkezete a gépi kódú utasításokhoz viszonyítva sokkal összetettebb, képzési módjuk sokkal inkább követi az emberi gondolkodásmódot. A magas szintű programozási nyelvek utasításai több gépi utasítást, tehát egy teljes utasítássorozatot takarnak, nem veszik figyelembe a számítógép felépítését. Ennek megfelelően a magas szintű fordítóprogramok is bonyolultabbak. A magas szintű programozási nyelvek gépfüggetlenek, tehát átvihetőek a forrásprogramok az egyik gépről a másikra. Ennek csupán egyetlen feltétele van, az, hogy az adott gépen az adott operációs rendszer alatt legyen ugyanolyan fordító. Minden programozási nyelvnek létezik egy úgynevezett hivatkozási nyelve (például Standard Pascal), amelyet – ideális esetben - a nyelv implementációi (vagyis a konkrét fordítóprogramok illetve értelmezők) megvalósítanak. Ha csak a hivatkozási nyelv által definiált eszközöket használjuk, akkor a program - forrás szinten - változtatás nélkül átvihető az egyik típusú gépről a másikra, egyébként, ha kihasználjuk az implementációnak a hivatkozási nyelvtől eltérő (esetleg azzal ütköző) lehetőségeit, akkor a programot módosítani kell egy másik implementációra való átvitelhez.

Számítógépes program írásakor az adott nyelv szintaktikája alapján készítjük el a programot. A program kódolásától a végrehajtásáig vezető leggyakoribb utat a következő ábrán szemléltetjük:

45. Ábra

A fordítást a programozó indítja el. A forrásprogramot a fordítóprogram szintaktikailag ellenőrzi. Ha hibátlan, előáll a tárgyprogram, ellenkező esetben hibajegyzéket kapunk. A hibajelzések alapján a programozó javít, és újraindítja a fordítást. Sok esetben többszöri próbálkozás után áll elő a tárgyprogram.

A tárgyprogram önmagában még nem végrehajtható. Ezt a szerkesztőnek más néven kapcsolatszerkesztőnek (linkage editor) össze kell szerkesztenie egyéb, a nyelv, illetve a rendszerszoftver által biztosított rutinokkal. Az esetleges további (strukturális) hibák kiküszöbölése után áll elő a végrehajtható program.

Ha a végrehajtható programot futtatjuk, elkezdjük a program tesztelését, azaz a szemantikai (logikai, tartalmi) hibák kiküszöbölését. A program szemantikailag hibás, ha elindul ugyan, de nem azt csinálja, amit kell, esetleg menet közben le is áll. Az ilyen hiba sok gondot okozhat a programozónak, mivel az poloska módjára rejtőzik a programban. Ezért a hiba neve a poloska angol megfelelője után bug, megtalálása és kiirtása a debuggolás, az erre alkalmas segédprogram a debugger.

A programfejlesztés során ezeket a feladatokat egyre inkább úgynevezett integrált fejlesztői környezetben (IDE - Integrated Development Environment) hajtják végre. Az IDE a programozást jelentősen megkönnyítő, részben automatizáló programok összessége. Tartalmaznak egy szövegszerkesztőt a program forráskódjának szerkesztésére, egy fordítóprogramot vagy értelmezőt, fordításautomatizálási eszközöket, valamint nyomkövetési, és gyakran grafikusfelület-szerkesztési és verziókezelési lehetőségeket sok egyéb mellett. A komolyabbakhoz kiegészítők tömege érhető el, amelyek a rendszerfejlesztés egyéb fázisaiban, például a dokumentálásban, projektmenedzsmentben stb. nyújtanak nagy segítséget.

Ma már nagyon sok magas szintű programozási nyelvet ismerünk. Ezek között van általános és speciális célú. Az alábbiakban néhány, az elsők közül való és valamilyen szempontból nevezetes, harmadik generációs programozási nyelvet ismertetünk.

A BASIC (Beginner's All-purpose Symbolic Instruction Code) programozási nyelv a magas szintűek közül az egyik legegyszerűbb. Első verzióját is oktatási célból készítette Kemény János (1926-1992) magyar-amerikai tudós. A BASIC-nek azóta nagyon sok különböző változata született meg.

1954-ben az IBM-nél egy kutatócsoport hozta létre a FORTRAN (FORmula TRANslation - formulafordítás) nyelvet, ami egyike a legrégebben használatos magas szintű programozási nyelveknek. Ennek megfelelően elég sok változata létezik, amelyek túlnyomórészt a korszerűsítések eredményei. Elsősorban matematikai számítások, fizikai kutatóintézetekben szükséges számítások elvégzésére fejlesztették ki.

Szintén a legkorábban kifejlesztett programozási nyelvek közé tartozik az ALGOL60 és az ALGOL68 (ALGOrithmic Language). Elsősorban algoritmikus problémák megoldására fejlesztették ki. Az ALGOL megjelenése annyiban volt hatással a programozási nyelvekre, hogy még ma is van sok ún. "ALGOL"-szerűnek nevezett nyelv.

A 60-as években fejlesztették ki az USA Hadügyminisztériumának megbízásából a COBOL (COmmon Business Oriented Language – általános üzletorientált nyelv) programnyelvet. A 60-as, 70-es években a magas szintű nyelven írt programok többségét COBOL-ban írták. Elsősorban adatfeldolgozásra készült, ezen a területen még ma is használják. Hasonlít a normál beszédhez, utasításai állítások.

Az IBM a 60-as évek közepén új nyelvet tervezett, ami a PL/1 nevet kapta. Ebben a nyelvben a korábbi programozási nyelvek előnyeit próbálták egyesíteni. A nyelv ezért eléggé univerzális, s ebből következően célszerű használatához nagyon nagy gépre volt szükség. Egyik legfontosabb erénye az adatállományok igen jó, rugalmas, magas logikai szintű definiálása és kezelése, illetve hibakezelése. Ma már kevésbé használatos nyelv, az ESzR gépek korszakában volt népszerű (1969-85).

Ken Thompson (1943-) 1970-ben dolgozta ki a B nyelvet, aminek segítségével az első UNIX operációs rendszer készült. Mivel a B nyelv nem volt elég hatékony, ezért 1971-ben Dennis Ritchie (1941-) kifejlesztette a C nyelvet, amiből 1983-ban létrejött az első szabványos C, majd 1989-ben megszületett az ANSI C szabvány végleges változata. (Megjegyzés: Ezt C89-ként, illetve C90-ként is szokták emlegetni, ugyanis az ISO egy évvel később fogadta el. Van C99 is azóta.) C nyelven írják a UNIX operációs rendszert, és ehhez általában hozzátartozik a C fordító is. A C nyelv elterjedését segítette, hogy nagyon hatékony programozási nyelv és a C forrásprogramok elég jól hordozhatóak a különböző platformok között. Ha egy C programban csak szabványos elemeket használunk, akkor valószínűleg mindenhol futni fog, minimális átalakítással. (Manapság a C nyelv szerepét a C++ veszi át, ami a C nyelv objektumorientált kiterjesztése. Általában a C programok gond nélkül használhatóak C++ rendszerben is, és a fordítók többsége ismeri mindkettőt.) Szokás a C nyelvet alacsony szintű programozási nyelvként is említeni. Ez azonban csak azt jelenti, hogy általában a számítógép által támogatott adattípusokkal dolgozik. Támogat olyan alacsony szintű műveleteket is, amelyet más magas szintű programozási nyelvek nem támogatnak, illetve sajátossága, hogy támogatja a bitstruktúrák kezelését is. (Ez utóbbi tulajdonságai miatt sokszor kiváltja az assembly nyelvet.) A C nyelv azonban nem tartalmaz I/O utasításokat. E műveletek elvégzésére függvénykönyvtárakat hoztak létre. A C nyelv nem tartalmaz sztringek (karakterláncok), halmazok és listák kezelésére szolgáló műveleteket sem. A függvénykönyvtárak segítségével azonban több hasznos eszközhöz juthatunk, mint más programozási nyelvekben.

A programnyelvek következő generációját nevezték el negyedik generációs (4GL = 4th Generation Language) nyelveknek, mivel az ezekkel az eszközökkel történő programfejlesztés merőben más technikákat és módszereket kívánt a fejlesztőktől, mint a magas szintű programozási nyelvek.

A 4GL eszközök magas szintű programozási nyelvre épülő komplex, objektumorientált programfejlesztői rendszerek. A 4GL rendszerek a hagyományos programozási nyelvektől eltérően nem igénylik az összes elemi - különösképp a felhasználói felületekre vonatkozó - tevékenység implementációját. A programozó készen kap olyan elemeket, objektumokat, amelyekkel ezeket a gyakran időrabló feladatokat gyorsan és hatékonyan képes megoldani. Így például nem szükséges a felhasználói felületet a részletek szintjén utasításonként programozni, elegendő csak a képernyőn megjelenő elemeket megadni, és a fejlesztőrendszer ez alapján már automatikusan generálni tudja az űrlap (ablak) egy alapértelmezés szerinti felépítését.

Ezzel a módszerrel a 4GL eszközökkel történő fejlesztés során a programozó magasabb szintű absztrakcióval készítheti a programjait. Erre az úgynevezett komponens alapú fejlesztési módszer teremti meg a lehetőséget. A komponensek olyan előre gyártott építőelemek, melyeket a fejlesztőkörnyezet tartalmaz. A programozó a vizuális tervezőfelületen ezekből összeállíthatja a programja vázát, felhasználói felületét. Ezen a szinten a programkód is automatikusan generálódik, tehát a fejlesztő idejének nagyobb hányadát fordíthatja a speciális algoritmusok elkészítésére. Manapság a nagyobb rendszereket szinte kizárólag ilyen 4GL környezetben fejlesztik, és olyan kisebb alkalmazásoknál is egyre jobban teret hódít ezek használata, mint például az adatbáziskezelő rendszerek.

A következő fejlődési szakasz a programozási nyelvek tekintetében az ötödik generációs nyelvek, vagy „intelligens nyelvek”, amelyek kutatása, fejlesztése még folyamatban van. Igen nagy számítógépkapacitást igényel: elvileg az emberi gondolkodás leírása történne meg, és a természetes, emberi nyelvet transzformálnák a számítógép által értelmezhető formára.

A programozási nyelvek számának növekedésével egyidejűleg változtak a programozási módszerek is, amely a nyelvek egy másik csoportosítását teszi lehetővé.

A kezdeti programozási nyelveknél alkalmazott, az építőkockák elvén alapuló programfejlesztési módszer a moduláris programozás, amikor a számítógéppel megoldandó feladatot egymástól független részfeladatokra osztják fel. Az egyes a modulokat külön-külön lehet megírni, átvenni már megírt programokból (rutingyűjteményből), és a tesztelés is modulonként történik. Második lépésként a kész, jól működő modulokat egy egységbe illesztjük, ellenőrizzük a modulok közötti információcserét, valamint a vezérlésátadások helyességét. A modulokat szubrutinnak szokás nevezni. A szubrutin nem más, mint az utasítások olyan sorozata, amely az adott program keretein belül önálló logikai és szerkezeti egységet alkot (azaz részfeladatot lát el), és a program futása során többször is meghívható. A program a szubrutinokat hívja meg a megfelelő sorrendben, és biztosítja a megfelelő paraméterátadást.

A strukturált programozás esetén a legelső dolgunk, hogy a feladatot (a problémát), amire programot szeretnénk írni, kielemezzük. A nagy feladatot mindig kisebb részekre bontjuk, azokat pedig még kisebbekre, egészen addig, amíg el nem érünk valamilyen, tovább már nem bontható részfeladatig. Az ilyen, tovább már nem bontható részfeladatokhoz már kidolgozhatjuk a megfelelő algoritmust. Az algoritmusban csak meghatározott vezérlési struktúrákat használhatunk, és megírjuk hozzá a megfelelő programrészleteket. Az így kapott program könnyen javítható, hiszen hamar kideríthető, hogy a hiba melyik programrészben fordult elő, és a program fejlesztése is viszonylag egyszerű. Az egyik legismertebb magas szintű programozási nyelv, amellyel a strukturált programozás elve megvalósítható, a Pascal, amit eredetileg nem a gyakorlatban használatos programnyelvnek szántak, hanem a strukturált programozási elv oktatására kidolgozott eszköznek.

Az objektumorientált programozás (OOP) sok tekintetben a moduláris programozás továbbfejlesztett változata, mely háttérbe szorítja a strukturált programozást. Az egyes elemeket itt nem moduloknak, hanem osztályoknak nevezzük. Az egyes osztályok a modellezett világ azonos fajta tulajdonságokkal és azonos viselkedéssel rendelkező objektumait írják le. Az objektumorientált programozás jobban közelíti, utánozza a valóságot, mint elődei, mert jobban igazodik a tárgyhoz. Az osztály létrehozásakor definiáljuk a szerkezetét, megadva objektumainak tulajdonságait, és a szubrutinokat, melyek leírják objektumainak a viselkedését, így a későbbiekben mindent együtt kezelhetünk, ami az adott objektumhoz tartozik. Az így létrejött nyelv sokkal strukturáltabb, sokkal modulárisabb, ami megmutatkozik az adatok szerkezetében, valamint a fölösleges hivatkozások elhagyásában is.

Főbb tulajdonságai:

  • Egy rekord összekapcsolása eljárásokkal és függvényekkel. Ez jellemzi az új adattípust, az osztályt.

  • Az osztályok a korábban definiált osztályoktól adatokat és módszereket vehetnek át (örökölhetnek). Egy adott típusú objektum kompatibilis a szülő típusok objektumaival.

Az aspektusorientált programozás (AOP) is a moduláris programozás továbbfejlesztett változata. Bár a legtöbb aspektusorientált nyelv egyben objektumorientált is, az AOP elvei függetlenek az objektumorientáltságtól. Lényege, hogy a program modulokra bontásakor egyidejűleg több szempontrendszert is érvényesíthetünk. Ezzel a módszerrel el tudunk különíteni (külön modulba tudunk kiemelni) olyan programrészeket is, amelyek a moduláris programozás hagyományos eszközeit használva több modulban szétszóródva jelennének meg. A hagyományos eszközökkel ugyanis a programot csak egyetlen, kitüntetett szempontrendszer szerint bonthatjuk modulokra, viszont emiatt azok a programrészek, amelyek egy másik szempontrendszer szerint logikailag összetartoznának (a program egy vonatkozását hordozzák), így több modulban szétszóródva jelennek meg. Ezzel egyidejűleg jellemző probléma, hogy a modulokban emiatt összekeverednek a program különböző vonatkozásait hordozó elemek.

Az AOP úgy küszöböli ki a vonatkozások szétszóródását és keveredését, hogy a szétszóródó elemek is kiemelhetők egy modulba. Ilyenkor a modulban le kell írni, hogy az így kiemelt elemek a program mely pontjaira illesztendők be. Az ilyen modulokat rendszerint aspektusnak nevezik, a beillesztést pedig szövésnek. Ez történhet már a program fordításakor, vagy csak a futtatásakor is.