Programozás
Verzió: 1.0 Perényi Zsigmond 2000. február 28.
1. Bevezetés
1.1 A hardver és a szoftver jelentőségének változása
A számítástechnika hajnalában (1950-es évek) a hardvernek sokkal nagyobb volt a jelentősége, hiszen akkoriban magának a számítógépnek, mint szerkezetnek az öszzeállítása és működtetése jelentette a legnagyobb problémát. Akkoriban csak a hozzáértő szakemberek jutottak számítógép közelébe, így nem is volt cél, hogy kimondottan felhasználóbarát programokat írjanak. |
|
Ahogy a technológia fejlődött, úgy a számítógép (a hardver) egyre megbízhatóbbá és olcsóbbá vált, így fokozatosan eljutott a kisebb cégekhez, s mára a hétköznapi emberig. Tehát a felhaszánlók köre megváltozott: profi szakemberek helyett most gyermekek, tanárok, orvosok, titkárnők, pékek, és takarítónők ülnek a gépek előtt, és minden joggal elvárják, hogy a számítógépet a saját különböző céljaikra a lehető legkényelmesebben lehessen felhasználni, a legkevesebb tanulással.
De mivel a számítógép hardvere csupán egy halom vas és műanyag, ami önmagától semmi értelmes dologra nem képes, a fent említett hatalmas felhasználói igényeket a programoknak (szoftvereknek) kell biztosítani. A számítástechnika története során tehát a hangsúly áthelyeződött a hardverről a szoftverre. Mára a szoftvergyártás (vagyis a programozás) jelentős iparággá nőtt.
1.2 A hardver és a szoftver kapcsolata: a számítógépek működése
1.2.1 Számítógépek
A számítástechnika fejlődése során számtalan különböző próbálkozás született arra, hogy miként lehetne a számításokat valamilyen géppel elvégeztetni. Mindközűl a Neumann-elvű gépek bizonyultak a legsikeresebbnek, a modern elektronika pedig megteremtette a digitális technológia alkalmazásának műszaki feltételeit.
A Neumann-elvű számítógépek lényege, hogy mind az adatok, mind az elvégzendő feladat (kódolt utasítások formájában) a memóriában van eltárolva. A digitális szó azt jelenti, hogy mindent számokként ábrázolunk, és azokat dolgozzuk fel.
A ma körülöttünk lévő számítógépek mind ilyen elven működnek, legyen az PC, Macintosh, nagygép, laptop, palmtop, vagy akár mobiltelefon, CD játszó, vagy mikrohullámú sütő processzora. Ezért ettől a ponttól számítógépen Neumann-elvű digitális számítógépet értünk.
1.2.2 A programvégrehajtás menete
- A memória
- minden rekesze sorszámmal (címmel) rendelkezik, így a memória bármely rekesze megcímezhető, és így a processzor által közvetlenül elérhető.
- A CPU (központi feldolgozó és vezérlő egység, a ,,processzor'')
- feladata, hogy végighaladjon a program utasításain, és az előírt sorrendben végrehajtsa azokat. Az utasítások műveleteket végeznek az adatokon.
|
- a soron következő utasítás gépi kódja lehívódik a memórióból a CPU-ba
- dekódolás: megállapítja, hogy milyen adatokkal milyen műveletet kell elvégezni
- beolvassa a memóriából a szükséges adatokat, végrehajtja az előírt utasítást,
az eredményt pedig visszaírja a memóriába |
1.3 A programozás alapjai
1.3.1 A gépi kód
A memóriában az utasítások kódolva tárolódnak, az adott processzor ,,nyelvén'' (a különböző processzoroknak különböző utasításai vannak. Utasítás alatt itt egyszerű, elemi utasítást kell érteni, pl. 2 szám összeadása, szorzása, egy szám másolása egyik memóriarekeszből a másikba... ). Minden utasításnak van egy kódja (ez lényegében egy szám). Az adott utasításhoz meg kell adni, hogy milyen adatokkal számol, pl. az általa használt adatok memóriacímét (azt a sorszámot, amely memória rekeszben elhelyezkedik). Így lehet a programot számokkal kódolni. Az így kapott kód a gépi kód. A programozás tehát lényegében ennek a gépi kódnak a megalkotása.
1.3.2 Programozási nyelvek, fordító/értelmező
Ez természetesen nagy odafigyelést igénylő, fáradtságos munka, amit nehéz megtanulni, könnyű elhibázni. Ezért már korán rájöttek, hogy a programozó munkáját minél jobban meg kell könnyíteni. Ennek érdekében programozási nyelveket dolgoztak ki, hogy ne számokkal kelljen programozni, hanem sokkal emberközelibb módon, olyan nyelven, amelyben számok (kódok és címek) helyett angol szavak szerepelnek. Egy ilyen nyelvet azonban nem ,,ért meg'' a gép, ezért ezt le kell fordítani a gép nyelvére. Ehhez szükség van egy olyan segédprogramra, amely az általunk megírt programot gépi kóddá alakítja (ez a fordító, compiler), vagy lépésenként végrehajtja (ez az értelmező, interpreter).
|
fordító (compiler) |
értelmező (interpreter) |
jellemzője |
a program forrásszövegéből egy gépi kódú, futtatható programot (pl. EXE) állít elő, vagyis a programot lefordítja a gép nyelvére |
a program forrásszövegét sorról sorra értelmezi, ellenőrzi, és (ha tudja) végrehajtja |
szintaktikai (alaki) hibák kiszűrése |
fordítási időben (+) |
futási időben (-) |
az elkészült program végrehajtási sebessége |
gyorsabb (+) |
lassabb (-) |
a program hordozhatósága |
rosszabb (-) |
jobb (+) |
a fejlesztés körülményessége |
nehézkesebb (-) |
egyszerűbb (+) |
1.3.3 Programozási hibák, hibakeresés
Természetesen egy magas szintű nyelven írt program írása közben is véthetünk hibákat. Ez lehet:
- szintaktikai hiba (alaki): a program nem felel meg az adott nyelv szabályainak. Pl. elírás, elfelejtettünk egy zárójelet... Az ilyen hibákat viszonylag könnyű megtalálni.
- szemantikai hiba (tartalmi): a program lefordul, elindul, de nem azt csinálja, amit kell, esetleg menet közben le is áll a hiba miatt. Az ilyen hibák sok gondot okoznak, s poloska módjára búvnak meg a programban, ezért ezeket a hibákat a poloska angol megfelelője után bug-nak nevezik. Az ilyen hibák megtalálása és kiírtása (debuggolás)sokkal nehezebb feladat, erre külön segédprogramok (debugger-ek) állnak rendelkezésünkre.
1.4 A programozás módszereinek változása
A számítástechnika története során a programok készítésének módszerei és eszközei is fejlődtek - könnyebbé váltak. A főbb állomások:
- gépi kód
az adott gép processzorának ,,nyelvén'' : számok (utasítás- és operandus kódok) sorozata
- assembly nyelv
a gépi kód emberibb változata, ahol minden gépi kódú utasításnak egy kis angol neve van
- magas szintű nyelvek (pl. PASCAL, C, C++, Java)
könnyű bennük ismétlési és döntési szerkezeteket, valamint alprogramokat készíteni
- integrált fejlesztő eszközök
olyan programfejlesztő környezetek, amelyek megkönnyítik a programozásnál megszokott szerkesztés-fordítás-hibakeresés munkaciklust: a szövegszerkesztő (editor), fordító (compiler), és hibakereső (debugger) egy helyen találhatók és jól együttműködnek.
- vizuális fejlesztőeszközök
olyan integrált programfejlesztő környezetek, melyekben sok programozói feladat szinte néhány egérkattintásra egyszerűsödök: pl. megrajzoljuk a leendő program dialógus ablakjainak elemeit, a fejlesztő eszköz pedig automatikusan
- legenerálja a megfelelő kódot: (vagyis megírja helyettünk a program egy részét)
- dokumentációt készít...
1.5 Programozási nyelvek
1.5.1 Csoportosításuk, jellemzésük
A számítástechnikában használatos nyelveket a nyelv által megoldandó probléma alapján 2 nagy csoportra oszthatjuk:
- leíró jellegű nyelvek
egy állandó állapotot (tényeket) fogalmazhatunk meg bennük, nem pedig cselekvést. pl. HTML, VRML
- (procedurális) programnyelvek
algoritmikus cselekvést (tennivalók sorozatát) fogalmazunk meg bennük. Ilyen elven működnek a Neumann elvű számítógépek. Innentől nyelven elsősorban ilyen programnyelvet értünk.
A nyelv absztrakciós szintje alapján:
alacsony szintű nyelvek nehezebb a programírás, de a lefordítandó program közelebb áll az gép nyelvéhez (a gépi kódhoz), így nehezen hordozható (más típusú gépekre), viszont több beleszólásunk van a gépközeli dolgokba… pl. assembly
magas szintű nyelvek könnyebb programot írni (pontosabban egy emberközeli probléma megfogalmazása könnyebb). A nyelv által nyújtott kényelm és az gép utasításkészlete közötti űrt a fordító/értelmező programnak kell áthidalnia. pl. Java
1.5.2 A fontosabb programnyelvek
A ma is élő, legelterjedtebb általános nyelvek:
- assembly
- gépi kódú programozást segítő nyelv
- a megírt program nehezen horozható
- ma már csak az operációs rendszerek készítői, ill. a hardvergyártók programoznak ilyenben
- PASCAL
- eredetileg a struktúrált programozás tanulónyelvének szánták
- továbbfejlesztett változata, a TURBO PASCAL a modulok, illetve ma már az OOP-t is támogatja
- a valós életben a C miatt nem tudott érvényesülni, illetve a DELPHI-ben él tovább
- DELPHI
- alapja az objektum-orientált Turbo Pascal
- fő erőssége a korszerű és hatékony vizuális fejlesztőfelület
- fő korlátja, hogy egy céghez (Borland), és egy platformhoz (Windows) kötődik
- C
- alacsony és magas szintű nyelvként is szokták emlegetni, mert hatékony is, könnyű is programozni…
- minden (fontos) géptípusra van C fordító -> (viszonylag) hordozható nyelv
- régen minden általánosat C-ben írtak, ma a helyét egyre jobban átveszi a C++. Ahol megmarad, az a
- rendszerprogramozás
- C++
- a C nyelv objektum-orientált továbbfejlesztése
- ma már a legtöbb géptípusra van C++ fordító is -> elég hordozható nyelv
- napjainkban minden (általános) programot ebben írnak
- Java
- egy képzeletbeli (virtuális) Java-gép programnyelve
- szintaktikája nagyon hasonlít a C++ -éhoz
- a nyelv szabványos részét képezik a leggyakrabban kellő alapkönyvtárak,
mint pl. a grafika, felhasználói felület, hálózat programozás, adatbáziskezelés….
- Java nyelven lehet appleteket írni, amelyek beszúrhatók HTML oldalakba is
- tökéletesen hordozható
- az Internet-programozás fő nyelve
Érdekességképpen néhány régebbi programnyelv, amelyek elterjedtek voltak, de ma már csak elvétve találkozni velük:
- FORTRAN (FORmula TRANslator)
- tudományos számítások
- legmagasabb szintű adatstruktúra a tömb
- ma főleg nagygépeken, vagy régi programok mélyén használják
- COBOL
- emberközeli szintaktika
- adatbáziskezelés
- ma (szinte kizárólag) nagygépen használják
- PROLOG ( = PROgramming in LOGic)
- matematikai logikán alapuló programozás
- logikai problémák megoldásánál hatékony, de másra nehézkesen használható
- mesterséges intelligencia kutatás
- LISP ( = LISt Processing)
- listafeldolgozás
- funkcionális programozás
- mai programok mélyén: pl. Emacs, AutoCAD
- SMALLTALK
- az első igazi objektum-orientált programozási nyelv
- interpreteres megoldás
2. Adatok
Mivel a programok utasításai adatokon dolgoznak, ismerkedjünk meg először ezekkel!
Minden adattípusnál meg kell nézni, hogy milyen értékeket képes tárolni, hogyan ábrázolja a számítógép, és végül milyen műveleteket végezhetünk velük.
2.1 Elemi (egyszerű) adattípusok
2.1.1 Logikai (boolean) típus
felvehető értékek |
ábrázolás |
false (hamis) |
0 |
true (igaz) |
nem 0, legtöbbször 1 |
műveletek |
asm |
C/C++, Java |
tagadás (negálás, invertálás) |
not |
! |
és |
and |
&& |
vagy |
or |
|| |
kizáró vagy |
xor |
^ |
2.1.2 Numerikus (számok) típusok
2.1.2.1 Egész, (fixpontos,) integer típus
Ábrázolása kettes számrendszerben történik. Egy kettes számrendszerbeli számjegy - egy bit. Az ábrázolható számtartomány attól függ, hogy hány biten (n) történik a tárolás:
|
ábrázolható számtartomány |
előjel nélküli |
0 .. 2n-1 |
előjeles |
-2n-1 .. 2n-1-1 |
Például egy n=16 bites előjeles egész esetében ez -215..215-1, vagyis -32768..32785. A mai gépek általában 8, 16, 32, 64 biten tárolják az egész számokat.
Műveletek egész számokkal |
műveleti jel |
megjegyzés |
negálás |
- |
|
összeadás, kivonás, szorzás |
+ - * |
|
(egész) osztás |
/ |
7/2=3 |
maradék (modulus) képzés |
% |
7%2=1 |
növelés (inkrementálás), csökkentés (dekrementálás) |
++ -- |
x++ jelentése: x = x + 1 x-- jelentése: x = x - 1 |
Bitműveletek |
|
műveleti jel |
tagadás (negálás, invertálás) |
not |
~ |
és |
and |
& |
vagy |
or |
| |
kizáró vagy |
xor |
^ |
shift (eltolás) balra |
shl |
<< |
shift (eltolás) jobbra |
shr |
>> |
2.1.2.2 Valós (lebegőpontos) típus
|
neve |
egyszeres pontosságú |
float |
dupla pontosságú |
double |
Gépi ábrázolása normál alakban: Például: 0.00123 = 0.123 * 10-2 (megadása 0.123e-2)
Műveletek: + - * / Összehasonlítás: a kerekítések és az ábrázolási pontatlanságok miatt használjuk az |a - b| < epsilon formulát!
2.1.3 Alfabetikus (betű alapú) típusok
2.1.3.1 Karakter (character) típus
ábrázolása |
kódolás |
7/8 bit |
ASCII |
2 byte |
UNICODE |
Karakterkonstans: aposztrófok között, pl. : 'c', '\n' (az utóbbi az új sor karakter)
2.1.3.2 String (szöveg) típus
Ábrázolása: karaktertömb/karakterfüzér.
Stringkonstans: dupla aposztrófok között: "Ez itt egy sor\n"
2.2 Összetett adatok
2.2.1 Tömb
Adott számú (általában azonos típusú) adat együttese.
Dimenzió szerint:
- 1 dimenziós: vektor
- 2 dimenziós: mátrix
- több dimenziós
Például:
- egy számokból álló 5 elemű egydimenziós tömb:
- speciális tömb: karaktertömb (string)
Indexelés:
A tömb minden elemének van egy sorszáma (ezt indexnek nevezzük). A indexelés (vagyis a sorszámozás) általában 0-val kezdődik. Az egyes elemeket úgy érhetjük el, ha a tömb neve után szögletes zárójelben megadjuk indexüket: a t tömb második eleme t[2] (a fenti példában t[2] értéke 0). Tehát egy t nevű n elemű tömb első eleme t[0], utolsó eleme t[n-1]. Az indexeléssel a tömbelemek úgy használhatók, mint a közönséges változók.
2.2.2 Rekord/struktúra
Több, különböző títpusú adat együttese. Az összegyűjtött adatok a rekord/struktúra mezőit képzik.
Például egy személy adatai:
mező neve |
típus |
nev |
string |
szuletesiEv |
integer |
vanErettsegije |
boolean |
Az egyes mezőkre általában ponttal elválasztva hivatkozunk, pl. ha az ember egy fenti személy típusú változó, akkor az adott ember nevét jelentő mező: ember.nev
2.2.3 Objektum
A rekordok/struktúrák továbbfejlesztése: az adattagokon (adatmezőkön) kívül tagfüggvényeket (metódusokat) is tartalmaznak.
3. Algoritmikus programozás
3.1 AlgoritmusAz algoritmus olyan pontos előírás, amely megmondja, hogy egy adott típushoz tartozó összes feladat (problémaosztály) megoldásához milyen műveleteket kell meghatározott sorrendben elvégezni (vagyis pontosan megmondja, hogy miként kell megoldani a problémát).
Kívánalmak:
- véges lépésszámon belűl érjen véget
- egyértelmű eredményt szolgáltasson
- általános: ne csak egy konkrét feladatot oldjon meg, hanem lehetőleg minél szélesebb problémaosztályt
(pl. ne csak egy konkrét másodfokú egyenletet, hanem bármilyen másodfokú egyenletet)
Az egyes lépésekben elvégezhető műveletek Neumann-elvű számítógépek esetében:
- egyszerű utasítások:
- összetett utasítások:
- utasítás blokk (több utasítás egyben)
- vezérlési szerkezetek (program menetének befolyásolásához használhatók):
- elágazások: döntési szerkezetek
- ciklusok: ismétlési szerkezetek
Leírása:
- algoritmus leíró nyelv
- blokkdiagram
- struktogram
3.2 Az értékadás
Elemi művelet, amely általában azonos típusú adatok között használható. Segítségével valamely változóhoz új értéket rendelhetünk. Általános alakja:
változó = kifejezés
Olvasd: változó legyen egyenlő kifejezés. Jelentése: először a jobb oldali kifejezés kiértékelődik, majd az eredmény betöltődik a bal oldalon lévő változóba. A kiértékelendő kifejezés általában tetszőlegesen bonyolult lehet, és tartalmazhat:
- konstansokat (pl. 3)
- változókat (pl. x)
- függvényhívásokat (pl. sin(x))
- operátorokat (műveleti jeleket) (pl. x + 2)
Pl:
z = y + 2*tan(x)
jelentése: számítsd ki az y + 2*tan(x) kifejezés aktuális értékét, és az eredményt tedd bele az z változóba!
Ha az egyenlőség jel (pontosabban az értékadás operátor) két oldala eltérő típusú, az alábbi dolgok történhetnek:
- hibajelzést kapunk (gyakran már fordításkor)
- automatikus típuskonverzió történik: az egyenlőségjel jobb oldalán lévő kifejezés kiértékelése során kapott eredmény az egyenlőségjel bal oldalán lévő változóval megegyező típussá konvertálódik.
Például JavaScriptben a szo = 5/10 értékadásnál a bal oldal string, tehát a jobb oldal eredménye (a 0.5 szám) a "0.5" stringgé konvertálódik.
Különböző nyelvek eltérő szigorral kezelik a típusok keveredését. Ezek alapján beszélhetünk
- erősen típusos nyelvekről (pl. Pascal, C++, Java)
- gyengén típusos nyelvekről (pl. JavaScript)
3.3 Függvények
A programozásban a függvények a matematikában használt függvények kiegészítései. Egy függvény jellemzői:
- van(nak) bemenő paraméter(ei)
- csinál valamit
- legfeljebb egyetlen visszatérési érték lehet (tehát vagy nincs, vagy 1 db van)
Például:
- a sin(x) függvénynek egyetlen valós paramétere van, a kapott paraméter szinuszát számolja ki, és ez az érték a visszatérési értéke.
- a write(s) függvénynek egyetlen (string) paramétere van, amelyet kiír a képernyőre, visszatérési értéke pedig nincs.
Függvényeket magunk is készíthetünk. Pl: kör kerületét kiszámító függvény (algoritmus leíró nyelven):
függvény KörKerület ( sugár: Valós )
kerület = 2 * sugár * 3.14; visszatérés: kerület vége
Fejlesztői könyvtárak (library-k, lib-ek)
Hasonló témájú függvényeket gyakran helyeznek el együtt, úgynevezett programozói könyvtárakban, library-kben. A könyvtárak (mint külön elkészített modulok) eredetük szerint:
- ,,beépített'': az adott fejlesztői környezethet (fordítóhoz) adják, pl. rendszerhívások
- vásárolható, vagy beszerezhető (pl. az Internetről letölthető)
- saját készítésű
téma szerint például:
- matematikai dolgok: pl. szögfüggvények... - ezeket általában a fejlesztői környezet adja
- felhasználói felület (user interface), ablakok programozása, pl:
- MFC: (Microsoft Foundation Classes) Windows programozáshoz
- AWT: Java programozáshoz
- 3D grafika
- OpenGL: (Silicon Graphics által)
- Direct3D: (Microsoft által)
- Java3D
- rendszerhívások: az operációs rendszer szolgáltatásait lehet segítségükkel elérni (pl. programból hozhatunk létre vagy törölhetünk fájlokat...)
- hálózatkezelés (pl. socket interface a TCP/IP-hez)
Természetesen nem lehet minden könyvtárat minden nyelvből elérni. Minden nyelvhez általában külön könyvtárak tartoznak.
3.4 Vezérlési szerkezetek
3.4.1 Elágazás (if)
A leggyakrabban használt elágazás az "if" (ha-különben típusú elágazás):
blokkdiagram |
stuktogram |
algoritmus leíró nyelv |
JavaScript |
|
|
ha (feltétel)
utasítások különben
utasítások |
if (feltétel)
utasítások else
utasítások |
3.4.2 Ciklusok
3.4.2.1 Hátultesztelős ciklus (do-while)
Először végrehajtja a ciklusmagban lévő utasításokat, majd megvizsgálja a feltételt (bennmaradási feltétel), és ha ez igaz, akkor újra végrehajtja a ciklustörzset. Vegyük észre, hogy ha ez az első lefutásnál már hamis, attól még a cikosmag mindenképp lefut egyszer.
blokkdiagram |
stuktogram |
algoritmus leíró nyelv |
JavaScript |
|
|
ciklus
ciklusmag amíg (feltétel) |
do
ciklusmag while (feltétel) |
3.4.2.2 Elöltesztelős ciklus (while)
Először megvizsgálja a feltételt (belépési feltétel), és csak akkor hajtja végre a ciklusmagban lévő utasításokat, ha a feltétel igaz. Vegyük észre, hogy ha kiinduláskor már hamis a belépési feltétel, akkor egyszer sem fut le a ciklustörzs.
blokkdiagram |
stuktogram |
algoritmus leíró nyelv |
JavaScript |
|
|
ciklus amíg (feltétel)
ciklusmag |
while (feltétel)
ciklusmag |
3.4.2.3 For ciklus
Az elöltesztelős ciklus egy speciális esete, amikor a megadott ciklusváltozó (gyakran i-nek nevezik) értéke minden lépés után automatikusan nő eggyel, a kezdőértéktől (k.é.) a végértékig (v.é.).
blokkdiagram |
stuktogram |
algoritmus leíró nyelv |
JavaScript |
|
|
ciklus i = k.é. től v.é. -ig
ciklusmag amíg (feltétel) |
(ez egy kicsit több annál)
for (i = k.é. ; i<v.é. ; i++)
ciklusmag |
|