Download as pdf or txt
Download as pdf or txt
You are on page 1of 418

1.

) Mi az eredménye a következő utasításnak: jmp vissza


- a program a vissza címkénél folytatódik tovább
- az utasítás címe előtt és után is lehet a vissza címke

2.) Mi az eredménye a következő utasításnak: rjmp vissza ?


- az utasítás címe előtt és után is lehet a vissza címke
- a program a vissza címkénél folytatódik tovább

3.) Mi az eredménye a következő utasításnak: call vissza


- direkt ugró utasítás hajtódik végre
- a vissza szubrutin kerül meghívásra

4.) Mi az eredménye a következő utasításnak: brne vissza


- feltételes ugró utasítás
- zero bit értékét figyeli, és ha nulla, akkor ugrik a vissza címkére

5.) Mi az eredménye a következő utasításnak: brcs vissza


- a carry bit értékét figyeli, és ha egy, akkor ugrik a vissza címkére
- feltételes ugró utasítás

6.) Mi az eredménye a következő utasításnak: breq vissza


- zero bit értékét figyeli, és ha egy, akkor ugrik a vissza címkére
- feltételes ugró utasítás

7.) Mi az eredménye a következő utasításnak: brcc vissza


- carry bit értékét figyeli, és ha nulla, akkor ugrik a vissza címkére
- feltételes ugró utasítás

8.) Mi az eredménye a következő utasításnak: ret


- a PC értéke a STACK tetejéről visszatöltődik
- a szubrutinhívásból tér vissza a program

9.) Mi az eredménye a következő utasításnak: ret


- a PC-be (Program Counter) a Stack-ből a visszatérési cím betöltődik
- a szubrutin végrehajtás befejeződik

10.) Mi az eredménye a következő utasításnak: rcall subr ?


- a subr kezdőcíme előjeles egy bájtos tartományon belül kell lennie
- a szubritun a visszatérési címe a Stack-ben tárolódik

11.) Mi az eredménye a következő utasításnak: call subr ?


- a szubrutin visszítérési címe a Stack-ben tárolódik
- subr címkére ugrik a programvégrehajtás

12.) Mi az eredménye a következő utasításnak: icall subr ?


- a szubrutin visszítérési címe a Stack-ben tárolódik
- A Z regiszterben tárolt címre ugrik a programvégrehajtás
13.) Mi az eredménye a következő utasításnak: eor r16, r16?
- a zero bit logika 1 értékű lesz
- kinullázza az r16-os regisztert

14.) Mi az eredménye a következő utasításnak: ldi r16, 0?


- kinullázza az r16-os regisztert
- nem állítja a STATUS regiszter bitjét

15.) Mire használható a SUB utasítás? (SUB op1, op2)


- op1=op1-op2
- op1-ből az op2 értékét kivonja, a carry értékét nem veszi figyelembe

16.) Mire használható az ADC utasítás? (ADC op1, op2)


- op1=op1+op2+carry
- több bájtos összeadás részöösszeg képzésére

17.) Mire használható az ADD utasítás? (ADD op1, op2)


- op1=op1+op2
- op1 és op2 értékét összeadja, a carry értékét nem veszi figyelembe

18.) Mire használható a SBC utasítás? (SBC op1, op2)


- op1=op1-op2-carry
- több bájtos kivonás részművelet képzésére

19.) Mire használható a SBC utasítás? (SBC op1, op2)


- op1=op1-op2-carry
- op1-ből az op2 értékét kivonja, a carry értékét figyelembe veszi

20.) Mire használható a SBRS utasítás? (SBRS op1, op2)


- Kihagyja a következő utasítást, ha a vizsgált 1 értékű
- op1 az egy általános felhasználású regiszter (r0-r31), op2 az azon belüli bit
sorszáma

21.) Mire használható a SBRC utasítás? (SBRC op1, op2)


- Kihagyja a következő utasítást, ha a vizsgált 0 értékű
- op1 az egy általános felhasználású regiszter (r0-r31), op2 az azon belüli bit
sorszáma

22.) Mire használható a SBIC utasítás? (SBIC op1, op2)


- Kihagyja a következő utasítást, ha a vizsgált 0 értékű
- op1 az egy PORT regiszter,b az azon belüli sorszáma

23.) Mire használható az CBI utasítás? (CBI op1, op2)


- op1 az egy PORT regiszter, b az azon belüli bit sorszáma
- 0-ba állítja az adott PORT regiszter bitjét

24.) Mire használható a SBI utasítás? (SBI op1, op2)


- op1 az egy PORT regiszter, b az azon belüli bit sorszáma
- 1-ba állítja az adott PORT regiszter bitjét
25.) Mire használható a SBIS utasítás? (SBIS op1, op2)
- op1 az egy PORT regiszter, b az azon belüli bit sorszáma
- Kihagyja a következő utasítást, ha a vizsgált 1 értékűt

26.) Mire használható a ROL utasítás? (ROL op1, op2)


- rotálás balra
- egy bájtos érték balra mozgatása során carry-be is kerül a legnagyobb értékű
bit

27.) Mire használható a ROR utasítás? (ROR op1, op2)


- rotálás jobbra
- egy bájtos érték jobbra mozgatása során carry-be is kerül a legkisebb értékű
bit

28.) Mire használható a CP utasítás? (CP op1, op2)


- T bitet nem állítja
- op1-op2, eredmény a STATUS regiszter bitjén

29.) Mire használható a SWAP utasítás? (SWAP op)


- Az alsó 4 bitet felcseréli a felső bit értékeivel
- 4 bites rotálást helyettesíti

30.) Mire szolgál a PUSH op utasítás?


- PUSH Rr 0<=r<=31
- SP <- SP-1

31.) Hogyan épül fel a megszakítás vektortábla az ATMEGA128 esetén?


- minden megszakítási számhoz egy 2 bájtos memória terület áll rendelkezésre
- 80h darab megszakítás kiszolgáló rutin címét lehet megadni

32.) Hogyan lehet engedélyezni az általánosan a megszakításokat az


ATMEGA128 esetén?
- SEI utasítás segítségével
- Status regsizter Interrupt bit 1-be állításával

33.) Hogyan lehet tiltani az általánosan a megszakításokat az


ATMEGA128 esetén?
- CLI utasítás segítségével
- Status register Interrupt bit 0-ba állításával

34.) Milyen kötelező szintaktikai elemei vannak egy megszakítás szolgáló


rutinnak?
- megszakítás kiszolgáló rutin elején elhelyezett címke, amelyre a megszakítás
vektortáblából ugrik adott megszakítás érvényre jutásakor
- reti

35.) Milyen beállítások szükségesek a TIMER0 interrupt használatához?


- inicializálni kell a STACK-et
- inicializálni kell a TIMER0 beállításait

36.) Milyen adatterületek közötti adatmozgásra használatos a OUT A,Rr ?


- OUT A,Rr 0<=r<= 31,0<=A<=63
- IO területen található regiszterek közötti adatmozgatás

37.) Milyen adatterületek közötti adatmozgásra használatos a MOV Rd,


Rr ?
- MOV Rd,Rr 0<=d<= 31,0<=r<=31
- általános felhasználású regiszterek közötti adatmozgatás

38.) Hol folytatódik a program a megszakítást kiszolgáló rutin


befejeződése után?
- reti hatására a Stack-ből kerül Pc-be a cím
- A megszakítás meghívása előtt a STACK-ben letárolt PC értéktől

39.) Hol található a megszakítás vektortábla az ATMEGA128 esetén?


- a programmemória 0-FFh címtartományban
- CSEG .ORG 0x0

40.) Mit jelent a direktíva?


- Program fordításának idején hajtódik végre..
- Előfordítónak szóló utasítás.

41.) Mi az overflow?
- Túlcsordulás.
- Előjeles számábrázolás esetén a számtartomány túllépését jelzi.

42.) Mi a carry?
- Átvitel.
- Előjeles számábrázolás esetén a számtartomány túllépését jelzi.

43.) Mi a címke?
- memóriacím, ahova ugró utasítás után a vezérlés átkerül
- memóriacím, amely címén vezérlés átadáskor folytatódik

44.) Mi a segéd átvitel bit szerepe?


- Az alsó 4 bitről a felső négy bitre való átvitelt jelzi.
- BCD számábrázolás esetén használható

45.) Mi az előjel bit szerepe?


- a negatív bit és a overflow bit kizáró vagy kapcsolata
- status regiszter bitje

46.) Mi a negatív bit szerepe?


- a legnagyobb bit érték másolata.
- status regiszter bitje

46.) Megbukok prog laborból?


- Problem?
- Challenge accepted!

47.) Mi az interrupt bit szerepe?


- Megszakítások esetén használatos.
- Általános megszakítás engedélyező bit.

48.) Mi jellemző az INC utasításra?


- az operandu értékét eggyel növeli
- aritmetika utasítás

49.) Mi jellemzi a DEC utasítást?


- az operandus értékét eggyel csökkenti
- aritmetikai utasítás

50.) Mi jellemzi az fread függfényt?


- Magas szintű fájlkezelésnél használatos
- 4 bemenő paraméterrel rendelkezik

51.) Mi jellemzi a read függvényt?


- alacsony szintű fájlkezelésnél hazsnálatos
- 3 bemenő paraméterrel rendelkezik

52.) A r16-os regiszter érték 4. bitjének vizsgálatára milyen maszkot


választana? (LSB a 0. bit)
- AND| r16, 0x10
- AND| r16, 16

53.) A felhasználói program 8 bites esetben


- A RAM-ban tárolja a változóit.
- A FLASH-ben fut.

54.) A FIFO és a megszakításos periféria kezelés


- Lehetővé teszi a sebesség eltérések kezelését.
- Lehetővé teszi a perifériák aszinkron kezelését.

55.) A jtag interfész 8 bites esetben


- Programozásra szolgál
- Debug-olásra szolgál.

56.) A megszakítási rutinban


- Tilos időzítőfüggvényt hívni.
- Tilos végtelen ciklust csinálni.

57.) Az állapotgépek (AVR8)


- Csak akkor lépnek ki az állapotgép függvényéből, ha egy állapot teljesült.
- Minden lekezelt állapot után kilépnek az állapotgép függvényéből.

58.) Az ineterrupt.h
- Definiálja a megszakítási vektorok nevét.
- Definiálja az ISR makrót.

59.) Az u8 adattípus
- Egy unsigned char típus.
- Általunk definiált 8 bites típus.

60.) Az io.h
- Az I/O regiszterek szöveges definícióit adja meg.
- A regiszter bitek nevét adja meg

61.) Az időzítők CTC megszakítása


- Folyamatosan küld megszakításokat a processzornak
- Használható időalapnak

62.) Az Atmega128 mikrokontroller


- Lebegőpontos múveleteket csak szoftveresen tud kezelni
- Ismeri hardveresen a fixpontos szorzást

63.) Az ISR makró


- Esetén az argumentumban meg kell adni amelyik vektorra vonatkozik.
- Elkészíti a megszakítási rutin fejlécét

64.) Az időalap
- Megszakításon keresztül hat a kontrollerre.
- Adja meg a kontrolleres rendszer alapidőzítését.

65.) Az AVR8 C fordítójában


- A char adattípus 8 bites
- Az int adattípus 16 bites

66.) Az AVR8 hardver megszakítás


- Egy hardver által kiváltott szubrutinhívás.
- Memóriában található ugrótáblán keresztül hajtódik végre.

67.) Magas szintű fájlkezelésnél a handler tartalma:


- Minden esetben memória cím
- Sikertelen fájlmegnyitás esetén NULL pointer

68.) Magas szintű fájlkezelésnél a handler tartalma:


- FILE típusú struktúrára mutató mutató
- Memóriacímet használ az állomány azonosítására

69.) Alacsony szintű fájlkezelésnél a handler tartalma:


- Int típusú
- Sikeres állománymegnyitás esetén pozitív érték

70.) Melyek az égész jellegű változók?


- unsigned char
- long
- int
- char

71.) A float adattípus


- képes törtek tárolására.
- 32 bites

72.) A ; operátor
- elválasztó operátor
- kifejezést zár le

73.) A && operátor


- logikai és operátor.
- relációs kifejezéseket kapcsol össze

74.) int i=15,j=10;


double r;
r=(double)/(i/j);
- az i/j egészként hajtódik végre
C programozási nyelv
munkapéldány

Dr. Schuster György

2011. március 3.

1
Tartalomjegyzék

1. Bevezetés 6

2. Szintaktikai elemek 7

2.1. Kommentek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.2. Változók . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.2.1. Változók tı́pusai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.2.2. Változók deklarálása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.2.3. Enumerity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.2.4. Tı́pus módosı́tók . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.2.5. Futásidejű konstans const . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.2.6. ”Hagyd békén” volatile . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.2.7. Tı́pus definiálás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.2.8. Változók inicializálása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.3. Operátorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.3.1. Elválasztó operátorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.3.2. Értékadó operátorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.3.3. Aritmetikai operátorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

2.3.4. Relációs operátorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

2.3.5. Logikai operátorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

2.3.6. Bit operátorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

2.3.7. Egyéb operátorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

2.3.8. Operátorok precedenciája . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

2.4. Futásidejű utası́tások . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

2.4.1. if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

2
2.4.2. if-else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

2.4.3. switch-case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

2.4.4. for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

2.4.5. while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

2.4.6. do-while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

2.4.7. break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

2.4.8. continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

2.4.9. goto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

2.4.10. return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

2.5. Függvények . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

2.6. A fordı́tás folyamata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

2.7. Előfeldolgozó utası́tások . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

2.7.1. #define . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

2.7.2. #undef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

2.7.3. #include . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

2.7.4. #if, #endif, #else, #elif, #ifdef, #ifndef . . . . . . . . . . . . . . 37

2.7.5. #pragma, #warning, ## . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

2.8. Mutatók . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

2.8.1. Alaptı́pusok mutatói, cı́mképzés . . . . . . . . . . . . . . . . . . . . . . . . . . 39

2.8.2. Indirekció . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

2.8.3. Függvény mutatók . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

2.8.4. A void pointer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

2.9. Összetett adatszerkezetek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

2.9.1. Tömbök . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

2.9.2. Többdimenziós tömbök . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

3
2.9.3. Pointer aritmetika . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

2.9.4. Sztringek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

2.9.5. Struktúrák . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

2.9.6. Tı́pusként definiált struktúra . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

2.9.7. Bitmező struktúrák . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

2.9.8. Rekurzı́v struktúrák . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

2.10. Unionok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

2.10.1. Tı́pusként definiált unionok . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

2.11. Modulok, moduláris programozás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

3. I/O kezelés 63

3.1. Standard I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

3.1.1. printf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

3.1.2. scanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

3.2. Fájl kezelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

3.3. Alacsony szintű fájlkezelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

3.3.1. open függvén . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

3.3.2. close függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

3.3.3. read függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

3.3.4. write függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

3.3.5. A tell és az lseek függvények . . . . . . . . . . . . . . . . . . . . . . . . . 76

3.3.6. fcntl függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

3.4. Magas szintű fájlkezelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

3.4.1. fopen függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

3.4.2. fclose függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

3.4.3. getc és putc függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

4
3.4.4. fprintf függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

3.4.5. scanf függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

3.4.6. fflush függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

3.4.7. fread és fwrite függvények . . . . . . . . . . . . . . . . . . . . . . . . . . 82

3.4.8. ftell és fseek függvények . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

3.5. Átjárás a két fájlkezeéls között . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

3.5.1. fileno függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

3.5.2. fdopen függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

3.6. Amire figyelni kell (intelmek a fájlkezeléssel kapcsoltaban) . . . . . . . . . . . . . . . . 87

4. Dinamikus memória kezelés 88

4.1. A memória lefoglalása malloc , calloc , realloc . . . . . . . . . . . . . . . . 88

4.2. A lefoglalt memória felszabadı́tása free . . . . . . . . . . . . . . . . . . . . . . . . 89

4.3. Amire figyelni kell (intelmek dinamikus memóriakezelés esetén) . . . . . . . . . . . . . 89

5
1. Bevezetés

A C programozási nyelvet Dennis Ritchie és Ken Thompson fejlesztette ki az 1970-es évek elején. Céljuk
az volt, hogy a UNIX operációs rendszert könnyen hordozhatóvá tegyék különböző számı́tógépek között.

Mivel az ”ős” UNIX assembly nyelven készült a hordozhatóság nehézkes volt, ezért a fejlesztők olyan
magasszintű programozási nyelvet kerestek, amely egyszerű, elég hatékony rendszer és hardver progra-
mozásra és ezek mellett alkalmas a operációs rendszerek ı́rására.

Mivel olyan nyelvet, amely mindezen követelményeket kielégı́tette nem találtak, ezért Ritchie Thompson
segı́tségével létrehozta a C programozási nyelvet. Ezzel a UNIX operációs rendszer lett az első olyan
operációs rendszer, amely magjának jeletős része magasszintű programozási rendszerben ı́ródott.

1978-ban megjelent a Dennis Ritchie és Brian Kernigham által ı́rt A C programozási nyelv cı́mű könyv,
amelyben az úgynevezett K& R C nyelvet ı́rják le. A fordı́tó programok nagy többsége ezt a C verziót
támogatja.

1983-ban az ANSI (American National Standards Institute) létrehozott egy bizottságot, amelynek az volt
a célja, hogy szabványosı́tsa és szabványos módon bővı́tse a C-t. Ezt a C verziót minden mai C fordı́tó
támogatja.

A C nyelv továbbfejlesztése a C++ nyelv, mely a C obejktum orientált ”kiterjesztése”.

Manapság UNIX és UNIX-szerű operációs rendszerek magját ma már szinte kizárólag C-ben ı́rják. A
beágyazott rendszerek firmware-i nagyrészt ebben készülnek. Így ez a programozási nyelv szinte min-
denütt jelen van.

Ez a jegyzet az ANSI C-vel foglalkozik. A példákban alkalmazott fordı́tóprogram a gcc. Ez a jegyzet


nem tér ki a nyelv nem szabványos könyvtáraira, illetve azokra a könyvtárakra, amelyek nem közvetlenül
szükségesek az alap működés megértéséhez.

6
2. Szintaktikai elemek

Minden programozási nyelv szintaktikai1 elemekből áll. Ezek általában jellemzők a nyelvre. A C nyelv
a következő szintaktikai elemeket tartalmazza:

• változók, változók módosı́tói és mutatók,

• operátorok,

• utası́tások,

• függvények,

• összetett adatszerkezetek,

• modulok.

A jegyzetben ezeket a fenti sorrend szerint ismertetjük2 .

2.1. Kommentek

Mielőtt a tényleges szintaktikai elemekbe belevágnánk beszéljünk egy pár szót a megjegyzésekről, vagy
kommentektől.

A C nyelv egy nagyon könnyen ı́rható, de elég nehezen olvasható nyelv. Ezért programunkat célszerű
megjegyzésekkel ellátni, amelyek a későbbiekben segı́thetik a programlistában a tájékozódást.

Az eredeti C nyelvben a kommentek /* */ párok közé kerülhettek. Ezek a fajta megjegyzések több
soron keresztül is tarthattak és a fordı́tó program teljesen figyelmen kı́vül hagyta őket.

Az ANSI C már megengedi az un. egysoros kommentet ez a // jellel kezdődik és a sor végéig tart.
Tehát csak egy sorra vonatkozik.

2.2. Változók

A programok általában valamilyen adatokon hajtanak végre műveleteket, illetve ezen műveletek eredményeit
is változókba helyezik. A legtöbb úgynevezett fordı́tott3 programozási nyelvben a változóknak tı́pusa
van.
1
Szintaktikai - helyesı́rási, vagy nyelvi elemek.
2
Sajnos ez azt is jelenti, hogy néha olyan programrészleteket vagy programokat kell áttekintenünk, amelyek elemei még
nem kerülhettek ismertetésre
3
A programozási nyelvek lehetnek un. interpreteres nyelvek, mint pl. a shell szkript, ahol a futtató program sorról sorra
fordı́tja és értelmezi az utası́tásokat a futás során. Továbbá lehetnek fordı́tott (compiled) nyelvek, ahol a programot a futtatás
előtt egy futtatható kóddá fordı́tják le, majd ebben a formában is tárolják.

7
2.2.1. Változók tı́pusai

Az ANSI C 5 öt alaptı́pust ismer, ezek:

• egész tı́pus: csak egészek tárolására szolgál, mérete implementáció függő (általában 16 - 32 bit), az
alkalmazott számábrázolás kettes komplemens. A tı́pus az un. egész jellegű változók csoportjába
tartozik, deklarációs megnevezése int, számábrázolási tartománya: 16 bit esetén -32768..32767,
32 bit esetén -2147483648..2147483647.

• karakter tı́pus: alapvetően karakterek tárolására szolgál, de kis egész számokat is tárolhatunk
bennük, mérete 8 bites, az egész jellegű változók csoportjába tartozik, deklarációs elnevezése
char,

• lebegőpontos tı́pus: ANSI szabvány szerinti egyszeres pontosságú lebegőpontos számok tárolására
szolgál, mérete 4 bájt, a lebegőpontos jellegű változók csoportjába tartozik, deklarációs elnevezése
float, számábrázolási tartománya: ±3.4 ∗ 10±38 ,

• ”dupla” lebegőpontos tı́pus: ANSI szabvány szerinti kétszeres pontosságú lebegőpontos számok
tárolására szolgál, mérete 8 bájt, a lebegőpontos jellegű változók csoportjába tartozik, deklarációs
elnevezése double, számábrázolási tartománya: ±1.7 ∗ 10±308 ,

• felsorolás tı́pus: deklarációs elnevezése enum

2.2.2. Változók deklarálása

Ahhoz, hogy bármilyen változót a programunkban használni tudjunk, azt előbb deklarálni kell. Ez fi-
zikailag annyit jelent, hogy a változó számára helyet foglalunk a számı́tógép memóriájában és az adott
tárterületnek megadjuk a tı́pusát is. A deklarációnak mindig meg kell előznie a felhasználás helyét. Ez
a C nyelvnél azt jelenti, hogy a deklarációnak meg kell előznie a működő program sorokat, akár minden
függvényen kı́vül, akár egy adott függvényen belül deklarálunk változót4 .

A változó neve csak betűvel és aláhúzás karakterrel (_) kezdődhet. A név tartalmazhat kisbetűt, nagy-
betűt, aláhúzást és számot. A kis és nagybetűk között a C különbséget tesz5 . A változó neve nem lehet
foglalt kifejezés.

Változó deklarálása formálisan a következőképpen történik:

tı́pus változóneve;

Az ANSI szabvány megengedi, egyetlen kifejezésben egyszerre, több (elvileg akárhány) azonos tı́pusú
változót is deklaráljunk:

tı́pus változóneve1,változóneve2,változóneve3;
4
A deklaráció helye a későbbiekben még szóba kerül.
5
Ez az un. Case Sensitive tulajdonság.

8
A következő példa egy egész változó függvényen belüli deklarációját mutatja be6 .

1 #include <stdio.h>
2 int main(void)
3 {
4 int i;
5 i=5;
6 return i;
7 }

Az i nevű változó deklarációja a main függvényen belül a 4. sorban történik.

Tehát:

int i; // egész változó deklarációja


char c; // karakter változó deklarációja
float f; // lebegőpontos változó deklarációja
double d; // dupla pontosságú lebegőpontos változó deklarációja

2.2.3. Enumerity

Az enumerity az úgynevezett felsorolás tı́pus. Ezt akkor célszerű alkalmazni, ha valamilyen növekvő
számsor egyes eleminek nevet szeretnénk adni. Ezek a számok csak egészek lehetnek. Vegyük példának
a hét napjait:

enum NAPOK{hetfo,kedd,szerda,csutortok,pentek,szombat,vasarnap};

Ekkor a hetfo értéket 0, a kedd értéke 1, stb. lesz.

Ha valamilyen okból a lista számait változtatni szeretnénk, akkor ezt a megtehetjük.

enum NAPOK{hetfo=1,kedd, szerda,csutortok,pentek,szombat=0,vasarnap=0};

Ebben a példában a hetfo értéke 1, a kedd értéke 2, a szombat és a vasarnap értéke 0 lesz.

2.2.4. Tı́pus módosı́tók

Más programozási nyelvekben járatos olvasóknak valószı́nűleg feltűnt, hogy a C nyelvben kevés változó
tı́pus van7 .
6
Ez a program működőképes, lefordı́tható és futtatható.
7
A sztring tı́pus is hiányzik, de arról majd később beszélünk.

9
Ezt az ellentmondást a C készı́tői úgy oldották fel, hogy az alaptı́pusokat úgynevezett módosı́tó prefixu-
mokkal módosı́tani lehet.

Az int módosı́tói:

unsigned ebben az esetben az int tı́pus ”előjeltelen” lesz. Tehát a tı́pus számábrázolási tartománya a
megvalósı́tástól függően 16 biten 0..65535, illetve 32 biten 0..4294967295. A deklaráció a közvet-
kező:

unsigned int ui;

long az ı́gy deklarált változó mérete nem lehet rövidebb, mint az eredeti int tı́pus mérete. Ezért
például 32 bites gcc esetén a long szintén 32 bites. 16 bites gcc esetén a long 32 bit szélességű.

short az ı́gy deklarált változó mérete nem lehet hosszabb, mint az eredeti int tı́pus mérete. Ezért
például 32 bites gcc esetén a long 16 bites. 16 bites gcc esetén a short 16 bit szélességű.

long long ez a módosı́tás nem szabványos a gcc fordı́tók azonban ismerik. Ez egy 64 bit szélességű
változót ı́r elő.

Az int prefixumainál nem szükséges kiı́rni deklarációkor az int kulcsszót. Vagyis:

unsigned int ui; kifejezésnek teljesen megfelel a unsigned ui;8 .

A C++ nem is engedi meg ilyen esetekben az int használatát.

A prefixumok keverhetők, tehát nyugodtan ı́rhatjuk, hogy

unsigned long ui;

Természetesen értelemszerűen. A long short auu; deklaráció nem helyes.

A char módosı́tói:

unsigned a karakter tı́pust előjeltelen 8 bites egészre változtatja, számábrázolási tartománya 0..255,

signed a karakter tı́pust előjeles 8 bites egészre változtatja, számábrázolási tartománya -128..127.

A karakter tı́pus esetén a char kulcsszó nem hagyható el. Ennek az az oka, hogy ha csak az unsigned
prefixumot használnánk, akkor a fordı́tó program ez unsigned int-nek értelmezné. A char köte-
lező a signed esetben is.
8
Ennek az az oka, hogy a Kernigham és Ritchie által definiált ”ős” C nyelv csak az int tı́pust ismerte, ı́gy fölösleges volt a
tı́pus kiı́rása.

10
A karakter tı́pus alapértelmezése a fordı́tó program beállı́tásától függ, és ez általában előjeles. Ha azon-
ban egy 8 bites előjeles egészre van szükségünk és a beállı́tás esetleg a megszokottól eltérhet célszerű
használni a signed kulcsszót9 .

A float tı́pusnak nincs prefixuma.

A double egyetlen prefixummal rendelkezik.

long egyes megvalósı́tásokban 10, másokban, például a gcc 12 bájt méretű lebegőpontos számábrázolást
ı́r elő.

2.2.5. Futásidejű konstans const

A const jelző bármilyen tı́pus és módosı́tott tı́pus esetén használható csak akkor célszerű a használata,
ha a változó értéket kap a deklarációnál. Példa:

const double PI=3.1415;

A fordı́tó program minden módosı́tási kı́sérletünkre hibaüzenetet küld.

2.2.6. ”Hagyd békén” volatile

A fordı́tó programok valamilyen szempontból optimalizálni próbálnak10 . Ha fölöslegesnek tartott változó


területeket találnak a kódban, azt egyszerűen kihagyják. Előfordul olyan eset, hogy mi ezt nem sze-
retnénk. Ekkor használjuk a volatile módosı́tót. Példa:

volatile int a;

Ez főleg moduláris programozás esetén lehet fontos (lásd ??. oldal.).

2.2.7. Tı́pus definiálás

A C lehetővé teszi, hogy új tı́pusokat definiáljunk valamely alaptı́pus11 segı́tségével.

Ha például szeretnénk egy előjeltelen 8 bites egész változó tı́pust mondjuk u8 néven, akkor a következőt
tehetjük:

typedef unsigned char u8;


9
Igen használjuk, nem PC-re ı́rt programoknál, de amikor mikrokontrollereket használunk, ahol olyan kevés a memória,
hogy minden bit számı́t sokszor tárolunk adatot char tı́pusban is.
10
Ez egy kicsit korai, de itt a helye (sajnos).
11
- és összetett adatszerkezet, illetve mutató -

11
A typedef kulcsszó mondja meg a fordı́tónak, hogy ha valahol egy u8 tı́pusú változó deklarációt talál,
akkor az tulajdonképpen egy unsigned char tı́pusú deklaráció12 .

Ezután egy változó deklarálása a következő módon történik:

u8 a;

2.2.8. Változók inicializálása

A változóknak adhatunk közvetlenül értéket.

Egész jellegű változók értékadása13 :

decimális szám i=65;

oktális szám i=0105;

hexadecimális szám i=0x41;

bináris szám i=0b01000101;

karakter kód i=’A’;

Lebegőpontos változók esetén egyszerűen értéket adunk a változónak. Néhány régebbi C fordı́tó megköve-
telte, hogy double és long double változó esetén a szám után egy L karaktert kellett ı́rni.

2.3. Operátorok

Az operátorok olyan szintaktikai elemek, amelyek egyszerű műveleteket ı́rnak elő, vagy kijelöléseket
végeznek. A C nyelvben az operátoroknak hét csoportja van, ezek:

1. elválasztó operátorok,

2. értékadó,

3. aritmetikai operátortok,

4. relációs operátorok,

5. logikai operátorok,

6. bit operátorok,

7. egyéb operátorok.
12
Akkor mire is jó nekünk ez a tı́pus definiálás? A válasz annyi, hogy a program jobban olvasható, követhető.
13
Ez az eredetileg egész jellegű változókat ezek módosı́tott és az ezekből definiált tı́pusokat jelenti.

12
2.3.1. Elválasztó operátorok

{}, (), [], ,, ;

{} a kapcsos zárójel pár egy logikai programrészletet zár magába. Ez lehet valamilyen szerkezet, pl.
switch-case, lehet ciklus törzs, elágazás esetén igaz, vagy hamis ág és lehet függvény törzs.

() a megszokott aritmetikai jelentéssel bı́ró zárójel precedenciát, vagyis művelet végrehajtási sorrendet
változtat meg. Például:
a=b+c*d; nem azonos a a=(b+c)*d; kifejezéssel.

[] tömb indexelő operátor. Lásd tömbök 42. oldal.

, vessző operátor. Részkifejezések elválasztására szolgál, de a kifejezéseket nem zárja le. Például:
c=a, a=b;
Ezt az operátort a makróknál fogjuk használni. Lásd ??. oldal.

; lezáró operátor egy adott kifejezést zár le.

2.3.2. Értékadó operátorok

Az értékadó operátorok enyhén eltérnek más programozási nyelvek értékadó operátoraitól, nevezetesen
tetszőleges tı́pusú változó értékét tetszőleges tı́pusú változónak adhatjuk át.

=, ?:, rekurzio

= egyszerű értékadás operátor. A baloldalon álló kifejezésnek adja át a jobb oldalon lévő kifejezés
értékét. Példa:
a=3;
FIGYELEM ez a kifejezés nem helyes: 3=a;

?: a feltételes értékadás operátora. Formálisan három kifejezésből áll a következő módon:


kifejezés1?kifejezés2:kifejezés3
ha a kifejezés1 igaz, akkor a kifejezés2, ha hamis a kifejezés3 hajtódik végre.
Például:
c=a>b?a:b;
Vagyis, ha a nagyobb, mint b, akkor c értéke legyen az a-ban tárolt érték. Ellenkező esetben c
kapja meg b értékét.

rekurzio ez az operátor család minden olyan binér14 operátoron alkalmazható, ahol valamilyen érték
keletkezik. A célja az, hogy az adott kifejezés rövidebb legyen. Példa:
14
Két operandusú operátor.

13
a=a+b; kifejezés helyett ı́rható a a+=b; kifejezés.
FIGYELEM gyakori a következő hiba:
a=b-a; kifejezés helyett a a-=b; kifejezést ı́rják.
Ez hiba, mert az a-=b; az a=a-b; -nek felel meg és ne feledjük, hogy a kivonás nem kommu-
tatı́v.
Az operátor csoport tipikusan az aritmetikai és bit operátoroknál használatos. Bizonyos megkötésekkel
a logikai operátorok esetén is lehet használni a rekurzı́v formát, de ez igen ritka.

2.3.3. Aritmetikai operátorok

Az aritmetikai operátoroknak van egy érdekes tulajdonsága. Ha aritmetikai műveletet hajtunk végre két
különböző tı́pusú változón, akkor a művelet a két tı́pus közül abban tı́pusban hajtódik végre, amelyben az
eredmény pontosabb15 . Tehát ha egy int és egy double tı́pusú változót adunk össze, akkor a művelet
double tı́pusban történik.

+, -, *, /, %, -, ++, --

+ az összeadás operátora,

- a kivonás operátora (binér operátor),

* a szorzás operátora,

/ az osztás operátora. Ez az operátor kı́ván némi figyelmet, mert ha két egész számot osztunk, akkor az
eredmény is egész lesz. Példa: a következő programrészletben két egész változó hányadosát adjuk
át egy lebegőpontos változónak.

int a,b;
double d;

a=8;
b=5;
d=a/b;

Első pillanatra az ember azt gondolná, hogy a d változó értéke 1.6 lesz, de nem. Mert az a/b
művelet egész tı́pusban hajtódik végre, aminek az eredménye 1, majd ez az 1 egész érték kon-
vertálódik át lebegőpontos 1 -é és kerül be a d változóba.

% a maradékképzés operátora. Csak egész jellegű változókra alkalmazható. Lásd a következő prog-
ramrészletet!
15
Szakszerűen a művelet a magasabb tárolási osztályban hajtódik végre.

14
int a,b,c;

a=8;
b=5;
c=a%b;

A c változó értéke 3 lesz, mert az 5 egyszer van meg a 8-ban és maradt 3.

- egyoperandusú mı́nusz Az operátor előjelváltást ı́r elő. Példa:


b=-a;
Vagyis b értéke a értékének a -1 szerese lesz.

++ inkrmentáló operátor16 . Az operátor egy változó értékét egyel megnöveli. Az operátor lehet posztfix,
vagyis állhat a változó neve után, vagy lehet prefix, vagyis állhat a változó neve előtt.
a++; Ez az operátor posztfix alkalmazása
++a; Ez az operátor prefix alkalmazása
A változó szempontjából a két mód azonos. Azonban ha az operátort összetett kifejezésben
használjuk a kifejezés teljes egészére az eredmény már különböző. Vegyük példának a következő
két programrészletet!

int a,b; int a,b;

a=5; a=5;
b=a++; b=++a;

Az egyértelmű, hogy mindkét programrészlet lefutása után az a értéke 6 lesz. A kérdés az, hogy a
b értéke mekkora
A baloldali programrészletben a b értéke 5 lett. A jobboldaliban pedig 6.
A magyarázat a ++ operátor un. kiértékelési irányából indul ki.
Nagyon egyszerűen magyarázva a bal oszlopban lévő programrészletben a b=a++; sora a követ-
kezőképpen működik. A program megtalálja az = operátort ez azt jelenti neki, hogy a bal oldali
változónak át kell adnia a jobb oldalon lévő kifejezés értékét. Ez a kifejezés most az a, tehát a b
értéke 5 lesz. Viszont a következő lépésben a program megtalálja a ++ operátort, ami a változóra
vonatkozik, tehát inkrementálja azt.
A jobb oszlopban lévő programrészlet először a ++ operátort találja meg. Ezután keresi azt a
változót, amire ez vonatkozhat. A bal oldalán az = van, tehát a jobb oldalon keres. Itt megtalálja
az a változót ezt inkrementálja és ennek az új értéket adja át a b-nek.

-- dekrementáló operátor. Az operátor egyel csökkenti a kérdéses változó értékét. Ezen kı́vül működése
mindenben megegyezik a ++ működésével.
16
az inkrementálás növelést jelent.

15
2.3.4. Relációs operátorok

A relációs operátorok a klasszikus összehasonlı́tó műveleteket jelölik ki. Ezeket az operátorokat az


elágazás (??. oldal) és a ciklus utası́tásokban (??. oldal) , illetőleg a feltételes értékadás operátor esetén
használjuk.

<, <=, ==, !=, >=, >

< kisebb,

<= kisebb, egyenlő,

== egyenlő. Eltérően más programozási nyelvekben ez az operátor a relációs operátor. Tipikus hiba,
hogy a programozó17 csak egyet ı́rnak. Erre az esetre a C fordı́tó egy figyelmeztetést küld, de nem
veszi hibának.

!= nem egyenlő,

>= nagyobb, egyenlő,

> nagyobb.

2.3.5. Logikai operátorok

A relációs kifejezések alapvetően relációs kifejezések összefűzésére szolgálnak. Érdekes tulajdonságuk


az, hogy csak addig dolgozza fel a program az összetett kifejezéseket, amı́g az egész kifejezés értéke el
nem dől.

Később erre mutatunk példát.

&&, ||, !

&& és operátor. Akkor igaz, ha az operátor mindkét oldalán álló kifejezés igaz. Például:
a>=5 && a<=10
vagyis a kifejezés akkor igaz, ha a értéke 5 és 10 zárt intervallumba esik.
Ha három kifejezést fűzünk össze, akkor a kifejezés addig megy mı́g van esély arra, hogy az igaz
legyen.
kif1 && kif2 && kif3
A kif2 kifejezés csak akkor hajtódik végre, ha a kif1 igaz volt. A kif3-ra csak akkor kerül a
vezérlés, ha a kif1 és kif2 igaz volt.
17
...palánták...

16
|| vagy operátor. Akkor igaz, ha az operátor valamelyik, vagy mindkét oldalán lévő kifejezés igaz.
Példa:

a<=5 || a>=10

Ha az a változó értéke 5, vagy annál kisebb, akkor a második kifejezésre nem kerül rá a vezérlés,
mert az eredmény már biztos. Ha a nagyobb, mint 5, akkor a jobboldali relációs művelet is
elvégzésre kerül.

! nem operátor. Az igaz kifejezés értékét hamisra, a hamist igazra változtatja.

MEGJEGYZÉS: a C nyelv igaznak vesz minden olyan értéket, amely nem 0 és hamisnak, amely 0 (lásd
a példát a 20. oldalon).

2.3.6. Bit operátorok

A bit operátorok bitmanipulációhoz használatosak. Csak és kizárólag egész jellegű tı́pusokon lehet
használni őket.

˜, &, |, ˆ, <<, >>

Az operátorok magyarázatához bemutatott példáknál a következő változókat használjuk inicializálva.

char a=0b00110101;
char b=0b00001111;
char c;

˜ egyes komplemens képző operátor. A művelet az adott pozı́ciókon lévő bitek közt hajtódik végre.
Példa:
c=˜a;
A c változó értéke a művelet után 11001010b.

& bitenkénti és kapcsolat operátora. A művelet az adott pozı́ciókon lévő bitek közt hajtódik végre.
Példa:
c=a&b;
A c változó értéke a művelet után 00000101b.

| bitenkénti vagy kapcsolat operátora. A művelet az adott pozı́ciókon lévő bitek közt hajtódik végre.
Példa:
c=a|b;
A c változó értéke a művelet után 00111111b.

17
ˆ bitenkénti kizáró vagy kapcsolat operátora. A művelet az adott pozı́ciókon lévő bitek közt hajtódik
végre. Példa:
c=aˆb;
A c változó értéke a művelet után 00111010b.

<< balra eltolás operátora. Az adott változó értékét tolja el balra (az MSB felé) adott számban. A
LSB-be 0 értékű bitek kerülnek. A kilépő bitek elvesznek. Példa:
c=a<<2;
A c változó értéke a művelet után 11010100b.

>> jobbra eltolás operátora. Az adott változó értékét tolja el jobbra (az LSB felé) adott számban. A
MSB-be 0 értékű bitek kerülnek. A kilépő bitek elvesznek. Példa:
c=a>>2;
A c változó értéke a művelet után 00001101b.

2.3.7. Egyéb operátorok

Ezek az operátorok egyetlen más csoportba sem sorolhatók. Nagy részüket nem is itt tárgyaljuk, hanem
azokon a helyeken, ahol már a szükséges szintaktikai elemek rendelkezésre állnak.

(cast), sizof(), &, *, ., ->

(cast) kikényszerı́tett tı́puskonverzió. Használata a következő programrészleten látható:

int a=6,b=5;
double d;

d=(double)a/b;

Ha a (double) konverzió nem lenne előı́rva az a változóra, akkor a d értéke a művelet után 1
lenne. A (double)a hatására az a változó a műveletben double tı́pusú változóként szerepel.
Ekkor a művelet double tı́pusban hajtódik végre és ezért d értéke 1.2 lesz.

sizeof() méret operátor Az operátor egy változó, vagy egy tı́pus méretét adja meg bájt méretben.
Példa:
a=sizeof(int); vagy a=sizeof(b);
Szerepe az int méretének meghatározásánál, illetőleg a tömböknél (42. oldal) és a struktúráknál
(47. oldal) lényeges.

& cı́mképző operátor. Részletesen tárgyaljuk a mutatók fejezetben (39. oldal).

* indirekciós operátor. Részletesen tárgyaljuk a mutatók fejezetben (39. oldal).

18
. mezőhozzáférés operátor. Részletesen tárgyaljuk a struktúrák fejezetben (47. oldal).

-> indirekt mezőhozzáférés operátor. Részletesen tárgyaljuk a struktúrák fejezetben (47. oldal).

2.3.8. Operátorok precedenciája

Az operátorok precedenciája fogalom azt jelenti, hogy az operátorok végrehajtási sorrendje milyen, ha
többszörös alkalmazás esetén nem teszünk zárójelet.

A kiértékelési iránya azt mondja meg, hogy az alkalmazott operátor a kifejezés mely részére vonatkozik.
Mint láthatjuk az unáris operátorok kiértékelési iránya a jobbról balra. Ez azt jelenti, hogy a kérdéses
művelet az operátorhoz viszonyı́tva jobboldalt található operanduson hajtódik végre.

Például: a b= a; kifejezésben a operátor a hozzá képest jobbra lévő a változón fejti ki hatását.

legerősebb {} () [] -> . balról jobbra


! ++ -- + - (cast) * & sizeof() jobbról balra unáris operátorok
* / % balról jobbra bináris operátorok
+ - balról jobbra
>> << balról jobbra
> < < = > = balról jobbra
== != balról jobbra
& balról jobbra
ˆ balról jobbra
| balról jobbra
&& balról jobbra
|| balról jobbra
?: jobbról balra
= += -= ... jobbról balra és a többi hasonló rekurzı́v
leggyengébb , balról jobbra

2.4. Futásidejű utası́tások

2.4.1. if

Az if utası́tás feltételes elágazást tesz lehetővé. Ha az utası́tás argumentumában szereplő kifejezés igaz
a program az un. igaz ágat végrehajtja, ha nem az igaz ág a program futása során figyelmen kı́vül lesz
hagyva.

Az igaz ág lehet egyetlen kifejezés, amely az első ; operátorig tart, vagy lehet egy logikai blokk, amelyet
a {} operátor pár zár közre.

A {} megoldást akkor kell használni, ha több ;-vel lezárt kifejezést szeretnénk az igaz ágba elhelyezni.
Természetesen, ha a program olvashatósága megkı́vánja akkor is alkalmazhatjuk, ha csak egyetlen ilyen
kifejezésünk van.

19
Példa az egyetlen kifejezésre:

if(a>5) printf("Nagyobb");

Ha a értéke nagyobb, mint 5, akkor kiı́rja, hogy Nagyobb18.

Példa a több kifejezést tartalmazó igaz ágra:

if(a>5)
{
b=a;
printf("Nagyobb");
}

Láthatjuk, hogy itt már két kifejezés szerepel, ezért szükséges a kapcsos zárójelpár alkalmazása.

FIGYELEM ne tegyél ; a záró kapcsos zárójel mögé!

Az if argumentumába általában relációs kifejezés kerül, de nem okvetlenül. A kiértékelés mindenképpen


relációs jellegű. Ha egy kifejezés értéke 0, akkor az hamisnak minősül, ha nem igaznak.

Tehát egy változó 0 értékének vizsgálata C programban általában ı́gy néz ki:

if(!a)

Ha a értéke 0, akkor hamis a ! operátor miatt ez igaz lesz, tehát a program belép az igaz ágba.

2.4.2. if-else

Az if használata csak azt tette lehetővé, hogy a program egy igaz ágat hajtson végre. Az else alkal-
mazása az if után lehetővé teszi a hamis ág alkalmazását.

Az else ág is egyetlen ;-vel lezárt kifejezésből, vagy egy {} által határolt logikai blokkból áll éppúgy,
mint az if esetén.

FIGYELEM az else utası́tásnak közvetlenül kell az if utası́tást követnie. Nem lehet közöttük sem-
milyen kifejezés.

Példa:

if(a>5)
{
b=a;
18
a printf függvénnyel most ne foglalkozzunk, ezt később tárgyaljuk részletesen.

20
printf("Nagyobb");
}
else
{
b=-a;
printf("Nem nagyobb");
}

Ha tehát az a változó értéke nagyobb, mint 5, akkor b felveszi a értékét és a printf kiı́rja, hogy
Nagyobb. Ha nem teljesül a feltétel, a program az else-re ugrik és b felveszi a mı́nusz egyszeresét és
a printf kiı́rja, hogy Nem nagyobb.

2.4.3. switch-case

Gyakran előforduló probléma, hogy egy változó értekétől függően a program különböző feladatokat
lát el. Ez a probléma megoldható ugyan az előzőekben megtanult if-else szerkezetekkel, de nem
kényelmes és elég nehezen követhető. Az ilyen ”szétugrási” feladatokban a kérdéses változó általában
egész jellegű.

Ezt a feladatot látja el a switch-case szerkezet.

A következő programrészlet bemutatja a switch-case felépı́tését

1 switch(a)
2 {
3 case 1: printf("Egy");
4 break;
5 case 2: printf("Ketto");
6 break;
7 case 3: printf("Harom");
8 break;
9 default:printf("Egyik sem");
10 }

Az 1. sorban a switch argumentumába kerül az a változó, ami az elágazás alapja.

Ha az a értéke 1, akkor a program a 3. sorra ugrik. A printf kiı́rja Egy, majd a program a 4. sorban
található break utası́tásra ugrik és elhagyja a szerkezetet.

Az a 2 esetén a program az 5. sorra ugrik. 3 esetén a 7. sorra kerül feldolgozásra.

Ha a értéke se nem 1, 2, vagy 3, akkor a program a default során folyatódik.

21
A default nem kötelező. Ha nincs default, akkor ilyen esetben a program kilép a switch-case
szerkezetből.

Nézzük a következő esetet!

1 switch(a)
2 {
3 case 1: printf("Egy");
4 case 2: printf("Ketto");
5 case 3: printf("Harom");
6 break;
7 default:printf("Egyik sem");
8 }

Látható, hogy az 1 és a 2 esetből hiányoznak a break utası́tások. Ekkor a 1 esetén kiı́rásra kerül
EgyKettoHarom, vagyis az adott case után a program nem hagyja el a szerkezetet, hanem fut tovább
egészen addig, amı́g nem talál break-et, vagy nem ér véget a szerkezet.

Tehát ha a értéke 2, akkor a képernyőre a KettoHarom kiı́rás kerül.

2.4.4. for

A for egy általános elöltesztelő ciklust létrehozó utası́tás. Argumentuma három kifejezésből áll.

A ciklustörzs hasonlóan az if utası́táshoz vagy egy ;-vel lezárt kifejezés, vagy egy {} operátorok közé
zárt logikai blokk.

A for formális alakja:

for(kif1;kif2;kif3) ciklustörzs

A működését a következő folyamatábra szemlélteti.

22
for

kif1

kif3 kif2

nem
Igaz?
igen

Ciklus
törzs
Vége

1. ábra: A for működése

A folyamatábrán látható, hogy az kif1 kifejezés csak egyszer hajtódik végre, ezért jól használható a
ciklus inicializálására

A második kifejezés (kif2) kiértékelése relációs jellegű és a ciklustörzs előtt található. Tehát a for-ral
létrehozott ciklus elöltesztelő. Ha a kif2 igaz a ciklus törzse lefut, ha hamis, akkor kilépünk a ciklusból.

A ciklus törzs után a harmadik kifejezés hajtódik végre.

A következő program 1-től 10-ig összeadja a számokat.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i,j;
5 j=0;
6 for(i=1;i<=10;i++)
7 {
8 j=j+i;
9 }
10 printf("%d",j);
11 return 0;
12 }

23
A 6. sorban látható a for utası́tás. Az első kifejezés az i váltózónak adja meg a kezdő értéket. A
második kifejezés egy összehasonlı́tás. Ez addig igaz amı́g az i változó értéke túl nem lépi az 10-et. A
harmadik kifejezés az i értékét növeli.

A 8. sorban képezzük az összeget.

Az 10. sorban ı́ratjuk ki az eredményt (lásd részletesen 63. oldalon).

Láthatjuk, hogy a ciklustörzshöz a {} megoldást választottuk, pedig itt csak egy kifejezés szerepel. Az
ok ı́gy jobban olvasható a program.

Ez a program csak C guruknak szól:

1 #include <stdio.h>
2 int main(void)
3 {
4 int i,j;
5 for(i=1,j=0;i<=10;i++) j+=i;
6 printf("%d",j);
7 return 0;
8 }

Azt láthatjuk, hogy ez a program jóval tömörebb. A tulajdonképpeni program csak az ötödik sor. Kérdés
az, hogy melyik program jobb. Talán a második megoldás egy hajszállal gyorsabb a += használata miatt,
de - és ez nagyon lényeges - az első programlista lényegesen jobban olvasható.

Ez már csak a teljesen elvadult guruknak:

1 #include <stdio.h>
2 int main(void)
3 {
4 int i,j;
5 for(i=1,j=0;i<=10;j=j+i++);
6 printf("%d",j);
7 return 0;
8 }

Na ez a lista már tényleg szörnyű. A működése gyakorlott programozónak triviális, azonban kezdőnek
csapdákat rejt magában.

Amit itt meg kell magyarázni az a j=j+i++;. Az j változóba a j előző értéke és az i aktuális értékének
összege kerül, majd ezután inkrementálódik az i.

24
Az lássuk be, hogy a ciklustörzs üres, az egész lényegi rész tulajdonképpen a 3. kifejezésben történik.

A for utası́tás esetén az összes belső kifejezés tetszőleges lehet. Nincs semmilyen megkötés arra, hogy
milyen tı́pusú változót használjunk, milyen aritmetikai, vagy logikai kifejezést használjunk.

Tipikus hiba a for zárójele után pontosvessző kerül. Ekkor a ciklustörzs a ; operátor lesz.

5 j=0;
6 for(i=1;i<=10;i++);
7 {
8 j=j+i;
9 }

EZ EGY HIBÁS LISTA!!!! a 6. sorban a ; lezárja a ciklust, ezért a végeredmény a várt 55 helyett 11
lesz. A ciklus ekkor csak az i értékét növeli 11-ig, majd ezt adja át j-nek.

2.4.5. while

A while utası́tás elöltesztelő ciklus létrehozására szolgál. Az utası́tás argumentumában lévő kifejezés
kiértékelése relációs jellegű. A program mindaddig belép a ciklustörzsbe, amı́g a kifejezés igaz.

A ciklustörzs itt is vagy egy ;-vel lezárt kifejezésig, vagy az utası́tást közvetlenül követő {} párba zárt
logikai blokk.

Készı́tsük el az első 10 egész szám összegét a while segı́tségével! A következő program futtatható.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i,j;
5 i=1;
6 j=0;
7 while(i<=10)
8 {
9 j=j+i;
10 i++;
11 }
12 printf("%d",j);
13 return 0;
14 }

Itt az 5. és a 6. sorban külön kell a változókat inicializálni. a 7. sorban van a while, aminek az

25
argumentumában egy relációs kifejezés van. Ha ez igaz, akkor a program belép a ciklustörzsbe.

A 10. sorban történik meg az összegzés.

A 11. sorban nekünk kell gondoskodnunk az i változó növeléséről.

Tipikus hiba itt is mint a for-nál a ; lerakása a while zárójele után.

EZ EGY HIBÁS LISTA!!!!

5 i=1;
6 j=0;
7 while(i<=10);
8 {
9 j=j+i;
10 i++;
11 }

A probléma ezzel a programmal az, hogy nem kı́vánt végtelen ciklusba kerül. Ennek oka az, hogy i
változó értéke nem fog változni, mert a program soha nem lép tovább a 7. sorról és a feltétel igaz marad.

2.4.6. do-while

A do-while szerkezet hátultesztelő ciklus létrehozását teszi lehetővé.

Hátultesztelő ciklus ciklus esetén az ciklustörzs legalább egyszer biztosan lefut, mert a ciklusban maradás
feltétele - röviden ciklus feltétel - a ciklustörzs végrehajtása után kerül feldolgozásra.

A ciklustörzs vagy egyetlen ;-vel lezárt kifejezés, vagy összetettebb esetben {} közé zárt logikai blokk.

Az egyszerű esetre példa:

1 int main(void)
2 {
3 int i=0;
4 do
5 i++;
6 while(i<10);
7 return 0;
8 }

A ciklustörzs itt az 5. sorban a i++; kifejezés.

26
A ciklus addig fut, amı́g a while argumentuma igaz.

Figyeljük meg, hogy a 6. sorban a while ;-vel le van zárva.

Az összetett esetre vegyük a megszokott példát a számok összegét 1-10-ig.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i,j;
5 i=1;
6 j=0;
7 do
8 {
9 j=j+i;
10 i++;
11 }
12 while(i<=10);
13 printf("%d",j);
14 return 0;
15 }

2.4.7. break

A break utası́tással már találkoztunk a switch-case szerkezetben. Itt is hasonló a szerepe, amikor
egy cikluson belül a program találkozik egy break utası́tással, akkor a program az aktuális ciklust
elhagyja. Ezt a futási gráfon lehet követni.

while(...)

if(...) break;

2. ábra: A break hatása

Láthatjuk, hogy a break hatására a program kiugrik a ciklusból19 .


19
Az if(...) azért szerepel a gráfban, mert ciklusba csak úgy nem ı́runk break utası́tást. Nem sok értelme lenne.

27
Nézzük azt az esetet, ha két ciklus van egymásba ágyazva és a belső ciklusban van a break utası́tás.

while(...)

while(...)

if(...) break;

3. ábra: A break hatása egymásba ágyazott


ciklusok esetén

A break szempontjából teljesen mindegy melyik ciklus utası́tást, illetőleg szerkezetet használjuk, a
működése azonos minden esetben.

2.4.8. continue

Ez az utası́tás is a ciklus szerkezetek futását módosı́tja. Hatására a ciklustörzs hátralévő részét a program
figyelmen kı́vül hagyja és a ciklus feltétel, illetve for esetén a 3. kifejezéssel folytatja futását20 .

A for működése continue esetén a következő folyamatábrán látható:


20
Régebbi szakkönyvek és jegyzetek azt ı́rják, hogy a ciklus újra kezdődik, ez nem igaz.

28
for

kif1

kif3 kif2

nem
Igaz?
igen

continue Ciklus
törzs
Vége

4. ábra: A continue hatása egymásba for ciklus esetén

A következő példa a continue működését mutatja be.

1 #include <stdio.h>
2 int main(void)
3 {
4 int i;
5 for(i=0;i<10;i++)
6 {
7 if(i==5) continue;
8 printf("%d ",i);
9 }
10 return 0;
11 }

A program 6. sorában lévő continue kihagyja a ciklus további részét és a 3. kifejezésre ugrik. Ezért
a program kimenete a következő:

0 1 2 3 4 6 7 8 9

Mint látjuk a printf függvény nem ı́rta ki az 5-ös értéket, mert nem került rá a vezérlés.

A printf ebben az esetben az i aktuális értékét ı́rja a képernyőre.

29
A következő két futási gráf bemutatja a continue hatását while és do-while esetén.

while(...) do

if(...) continue; if(...) continue;

while(...);

5. ábra: A continue hatása while


és do-while ciklusok esetén

2.4.9. goto

A goto utası́tás feltétel nélküli ugrás ı́r elő. Valahol a programba elhelyezünk egy cı́mkét és a goto-val
erre a cı́mkére ugorhatunk.

A cı́mke a következő, például:

CIMKE:

A goto-val az ugrás:

goto CIMKE;

A cı́mkére ugyanazok a szabályok vonatkoznak, mint a változónevekre, de azért szoktunk21 nagy betűs
cı́mkéket használni, mert nincsenek nagybetűs kulcsszavak és jól láthatók.

Figyelem a goto használata nagyon nem ajánlott. Átgondolatlan ugrással a programot tökéletesen ki
lehet fagyasztani. Csak akkor indokolt a használata, ha időzavarban van a program. De ez csak nagyon
ritkán fordul elő.

2.4.10. return

Ez az utası́tás arra szolgál, hogy visszatérjünk egy függvényből, illetőleg a visszatérés mellett értéket is
adjunk vissza a hı́vó félnek.

Mivel ez az utası́tás szervesen kapcsolódik a függvényekhez, ezért ez ott tárgyaljuk részletesen. Lásd
lejjebb.
21
Nem szoktunk egyáltalán nem szoktunk goto-t használni.

30
2.5. Függvények

A függvények tulajdonképpen szubrutinok.

A szubrutin egy olyan alprogram vagy programrész, amely a program bármely részéről többször meghı́vható.

A program a szubrutin hı́vás helyéről a szubrutin soraira ugrik. Amikor ezeket végrehajtotta a hı́vás utáni
soron folytatja futását.

A programozási nyelvek - beleértve az assemblereket is - kevés kivétellel ismerik a szubrutint.

A függvény tehát egy önálló programrész, amely valamilyen feladatot végez22 .

Minden C program tulajdonképpen egy függvény. Eddig minden C programunkban található egy int
main(void) sor. Ez az úgynevezett vezető függvény fejléce.

A C egy teljesen ”demokratikus” programozási nyelv minden függvény szintaktikailag mindenhonnan


hı́vható - még a main is.

A függvények ismertetését egy példán keresztül mutatjuk be. A függvény három egész változó közül
választja ki a legnagyobbat és azt adja vissza.

A következő programrészlet a függvény definı́ciója.

1 int max3(int a,int b,int c)


2 {
3 int i;
4 if(a>b) i=a;
5 if(c>i) i=c;
6 return i;
7 }

Az lista 1. sorában látható a függvény fejléce. Az első kifejezés int azt mondja meg, hogy milyen
tı́pusú változót fog visszaadni a függvény. Ezt nevezzük a függvény tı́pusának.

A következő kifejezés a függvény neve maxi ennek a névnek a segı́tségével fogunk hivatkozni függvényre.

A név utáni zárójelpárban a paraméterlista található. Itt deklaráljuk az úgynevezett átadott paramétereket.
Ezek a változók fogják a függvény bemeneti paramétereit tartalmazni.

A 2. sorban a függvény törzsét megkezdő kapcsos zárójelet találjuk.

A 3. sorban egy változót deklarálunk, amely átmeneti tárolóként fog szerepelni. Ez a változó egy
22
Néhány programozási nyelv, mint például a Pascal a szubrutinokat két csoportra osztja: eljárásokra, amelyek nem adnak
vissza értéket és függvényekre, amelyek értéket adnak vissza. A C esetében minden szubrutin függvény.

31
úgynevezett blokkra lokális változó (lásd ??. oldal), amely csak és kizárólag ebben a függvényben
látható és érhető el.

A 4. és 5. sorral most nem foglalkozunk23 , annak ellenére, hogy ez függvény működő része.

A 6. sorban láthatunk egy return utası́tást. Ennek az utası́tásnak az a szerepe, hogy a programot
kiléptesse a függvényből és az argumentumában lévő változó értékét visszaadja a hı́vó függvénynek.

Figyelem a definı́ció végén nincs pontosvessző!

Nézzük ennek a függvénynek a használatát egy programban!

1 #include <stdio.h>
2 int maxi(int,int,int);
3 int main(void)
4 {
5 int x,y,z;
6 int r;
7 scanf("%i%i%i",&x,&y,&z);
8 r=max3(x,y,z);
9 printf("%d",r);
10 return 0;
11 }
12 int max3(int a,int b,int c)
13 {
14 int i;
15 if(a>b) i=a;
16 if(c>i) i=c;
17 return i;
18 }

A 2. sorban láthatjuk a int max3(int,int,int); kifejezést. Ezt nevezzük a függvény dek-


laráció-jának

Itt megadjuk a függvény tı́pusát, a nevét és a paraméterlistában szereplő változók listáját sorrendben. A
deklarációt lezárjuk pontosvesszővel.

Az 5. és 6. sorban a main belső változóit deklaráljuk. Ezek a változók csak a main-ban ”léteznek”.

A 7. sorban a beolvassuk az x, y és z változókat (lásd ?? oldalon.).

A 8. sorban történik meg a max3 függvény meghı́vása. A meghı́vás során a main x változó értéke
belemásolódik a max3 a változójába, a y a b-be és a z a c-be.
23
Ezt már az olvasónak értenie kell.

32
A függvény végén a 17. sorban található return i; hatására a max3 i változó értéke a main r
változójába másolódik.

A printf kiı́rja r értékét.

A 10. sorban lévő return utası́tás az operációs rendszernek ad át egy úgynevezett errorkódot.

Mégegyszer!

• A függvény deklarációjánál megadjuk a függvény fejlécét pontosvesszővel lezárva Ekkor a fordı́tó


programnak mondjuk meg, hogy a függvényt a hı́vásokkor hogyan kell értelmezni.

• A függvény definı́ciója a függvény tulajdonképpeni megı́rása.

• A függvény tı́pusa annak a változónak a tı́pusa, amit a függvény visszaad.

Megjegyzés: a függvényeket néhány esetben nem kötelező deklarálni. Ezek az esetek, ha a függvény
definı́ciója megelőzi a függvény hı́vását (nem mindig teljesı́thető) és ha a függvény tı́pusa int. De a
kellemetlen figyelmeztetések és rejtélyes hibák elkerülésére erősen ajánlott a deklaráció. Ha valaki C-ről
áttér C++-ra és nem deklarál a figyelmeztetés helyett már hibaüzeneteket kap.

Felmerül a kérdés, hogy mi van azokkal a függvényekkel, amelyek nem adnak vissza értéket24 . Ezek az
úgynevezett void tı́pusú függvények.

Abban az esetben, ha a függvénynek nincsenek átadott paraméterei, akkor célszerű ezt is void-ként
megadni. Tehát például az előbb emlı́tett képernyőtörlő függvény deklarációja:

void clrscr(void);

A void tı́pusú függvényeknél nem kötelező a return utası́tás használata, a függvény egyszerűen csak
véget ér Azonban lehet használni. Ha egy void függvényből nem a végén akarunk kilépni, hanem
valahol ”útközben”, akkor egyszerűen használhatjuk a return; kifejezést.

Ez igaz akkor is, ha a függvény nem void mert a függvény bármely részéről visszatérhetünk egy
return-al - persze értelemszerűen.

A C nyelv nagyon széles függvénykönyvtárral rendelkezik. Alapesetben is egy gcc környezet több ezer
függvényt biztosı́t a felhasználó számára. Ezek a könyvtárak a fordı́tás során épülnek be a futtatható
kódba (lásd következő fejezet).

2.6. A fordı́tás folyamata

Igazából ez a témakör nem szigorúan tartozik a szintaktikai elemek közé, de ha gyorsan megbeszéljük
sokkal könnyebb az ezt követő elemek magyarázata
24
Igen vannak ilyenek. Egy képernyő törlésnek miért kellene értéket visszaadnia.

33
A fordı́tás folyamata a következő ábrán követhető.

prog.c

Előfeldolgozó

Átmenti
állomány

Fordító

prog.obj más .obj-tek könyvtárak indító kód

Linker

futtatható
kód

6. ábra: A fordı́tás vázlata

Kiindulunk egy egyszerű prog.c nevű forráskódból. Ebből szeretnénk futó programot készı́teni.

A fordı́tás első lépéseként a forráskódot egy előfeldolgozó (precompiler) kapja meg. Ennek feladata
az hogy a forrásból a fordı́tás számára lényegtelen dolgokat eltávolı́tsa - megjegyzéseket, fölösleges
szóközöket és zárójeleket - és hajtsa végre az előfeldolgozó számára szóló utası́tásokat.

Ekkor képződik egy átmeneti állomány, amit mi nem is látunk.

Az átmeneti állományból a fordı́tó program (compiler) készı́t egy úgynevezett prog.obj állományt.
Ez már egy lefordı́tott kód, de a bizonyos cı́mek és hivatkozások még nincsenek kitöltve.

A végleges fordı́tást a linker végzi, ami minden szükséges elemet hozzácsatol a kódunkhoz és az előzőleg
kitöltetlen adatokat is kitölti és ezáltal egy futtatható kódot állı́t elő.

Megjegyzés: ez azért egy nagyon vázlatos leı́rás.

34
2.7. Előfeldolgozó utası́tások

Az előfeldolgozó utası́tásoknak nincs közvetlen hatásuk a program futására. Ezek a fordı́tás folyamatát
befolyásolják.

Minden előfeldolgozó utası́tás # karakterrel kezdődik és nincs a végén pontosvessző.

2.7.1. #define

A #define szöveghelyettesı́tésre szolgál. Ezért fordı́tás idejű konstansokat és makrókat lehet vele
definiálni.

Elsőként nézzünk példát a fordı́tás idejű konstansra!

#define ZERO 0

Attól a ponttól kezdve ahonnan a ZERO szövegkonstanst definiáltuk, a program ismeri ezt. tehát ha a
programunkba leı́rjuk például, hogy:

a=ZERO;

akkor az előfordı́tás után az optimalizált kódba az kerül, hogy

a=0;

Ekkor csak egy egyszerű szöveghelyettesı́tés történik.

A makró is tulajdonképpen szöveghelyettesı́tés, de kicsit intelligensebb módon. Nézzünk erre is egy


példát!

#define MAX(a,b) (a>b)?(a):(b)

Ha a programunkba leı́rjuk a következő sort:

z=MAX(x,y);

akkor a következő szöveg kerül be a kódba előfeldolgozás után:

z=x>y?x:y;

a fölösleges zárójelek eltűntek és az a és b formális paraméterek helyére az x és az y változók kerültek.

Figyelem! ne feledjük a makró nem függvény, hanem csak szöveghelyettesı́tés. Nagyon szerencsétlen
hibát okozhat például egy ilyen makró hı́vás.

z=MAX(x++,y);

35
Ennek elemzését az olvasóra bı́zzuk. De ha lehet, akor kerüljük az ilyen eseteket.

Szintaktikailag akár konstans, akár makró definiálás estén a #define-t legalább egy szóköz értékű
karakternek kell lennie, aztán jöhet a szimbólum rész, amiben nem lehet szóköz jellegű karakter, majd a
helyettesı́tett rész, amiben lehet szóköz és vagy a sor végéig tart, vagy valamelyik megjegyzés mezőhöz.

Ha nem férünk el a helyettesı́tett résszel az adott sorban, akkor azt elválaszthatjuk egy \karakterrel. Erre
mutat példát a következő programrészlet:

#define RNG() (mx>x && mx<x+w &&\


my>y && my<y+h &&\
btn)

Így sokkal jobban olvasható a program és akár soronként megjegyzések is fűzhetők a kódba.

Ne tedd! Ha egy mód van egy makró legyen egyetlen kifejezés és a belsejében ne legyen pontosvessző
és ne legyen benne logikai blokk. Persze lehet, de azt nagyon fejben kell tartani, mert nagyon sok
kellemetlenséget okozhat.

2.7.2. #undef

Ha valamilyen okból akár a makrónkat, akár a fordı́tás idejű konstansunkat meg akarjuk szüntetni, mert
például felül akarjuk ı́rni, akkor az #undefutası́tást kell használni. Tehát például:

#undef ZERO

sor után a ZERO szövegkonstans nem létezik.

2.7.3. #include

#include utası́tás arra utası́tja az előfeldolgozót, hogy az utası́tás helyére szerkessze be az utası́tás
argumentumában szereplő fájl tartalmát.

Ezeket a fájlokat nevezzük header fájloknak.

Ennek két eltérő formája van. Történelmi okokból a gyári header fájlokat egy <> jelpár közé, a mi
általunk ı́rt header állományokat pedig "" pár közé tesszük.

Vannak olyan környezetek, amelyek ezen jelölés alapján különbséget tesznek fordı́táskor, de nem mind-
egyik. Célszerű ezt a konvenciót betartani.

A header fájlok függvény deklarációkat, makrókat, fordı́tásidejű konstansokat és tı́pusdefinı́ciókat tartal-
maznak. Tartalmazhatnak ugyan függvény definı́ciókat, de ez erősen kerülendő.

36
Példa a gyári header fájl behı́vására:

#include <stdio.h>

Példa a saját header behı́vására:

#include "enyem.h"

2.7.4. #if, #endif, #else, #elif, #ifdef, #ifndef

Az alfejezet cı́mében szereplő utası́tások a feltételes fordı́tás utası́tásai. A feltételes fordı́tást általában ak-
kor használjuk, ha a megı́rt programnak különböző környezetekben is jól kell működnie, vagy esetleg hi-
bakeresésé céljából speciális kódrészleteket kell beletenni a programba, amelyek később szükségtelenek,
stb.

A feltételes blokkok tetszőleges számban egymásba ágyazhatók.

Kezdjük a végén! Minden egyes feltételes fordı́tás blokk az #endif utası́tással ér véget.

Az #if utası́tás a kezdete egy feltételes blokknak. Ha az argumentumában lévő kifejezés igaz az #if-et
követő blokk lefordul, vagy az #endif, vagy a #else, vagy az #elif utası́tásig, vagy esetleg egy
újabb feltételes blokk kezdetéig.

#if DEBUG==1
|
fordul
|
#endif

Ha tehát a DEBUG paraméter értéke 1, akkor az #if és #endif közötti rész lefordul.

Ha a kifejezés nem igaz, akkor viszont egyáltalán rá se kerül a fordı́tóra, mert az előfeldolgozó nem
”engedte” tovább a kérdéses kódrészletet.

Az #else ág - ha van - akkor érvényesül, ha az #if-ben lévő kifejezés hamis.

#if 2==1
|
nem fordul
|
#else
|
fordul

37
|
#endif

Mivel a példában 2 nem egyenlő 1-el ezért a kifejezés hamis, tehát most az #else ág fordul be a végső
kódba.

Az #elif tulajdonképpen egy #else és egy #if. Ha az argumentumában lévő kifejezés igaz, akkor
egy új fordı́tási blokkot indı́t, viszont nincs szükség újabb #endif-re.

#if 2==1
|
nem fordul
|
#elif 2==2
|
fordul
|
#else
|
nem fordul
|
#endif

Az #ifdef egy olyan ”#if”, amely akkor ”igaz”, ha az argumentumában szereplő kifejezés már
előzőleg egy ”#define utası́tással már definiálásra került. Az értéke teljesen mindegy.

#ifndef akkor igaz, ha a kérdéses szimbólum nem definiált.

Mind az #ifdef, mind az #ifndef esetén a többi utası́tást azonos módon lehet - kell használni

2.7.5. #pragma, #warning, ##

A #pragma előfeldolgozó utası́tás arra szolgál, hogy a fordı́tó programnak különböző üzeneteket küldjünk.
Ezzel az utası́tással például egyes figyelmeztetéseket ki lehet kapcsolni.

A #warning utası́tás az argumentumában megtalálható szöveget fordı́tás során kiı́rja a képernyőre. Ez


nem szabványos C utası́tás.

A ##utası́tás két szövegrészt fűz össze a forráskódban. Például:

value##a=a;

Ekkor a fordı́tásra kerülő szöveg:

38
valuea=a;

2.8. Mutatók

A mutatók, vagy pointerek olyan speciális változók, amelyek nem értéket, hanem valamilyen más objek-
tumnak25 a cı́mét tartalmazza.

2.8.1. Alaptı́pusok mutatói, cı́mképzés

Azt mondtunk, hogy a mutató egy speciális tı́pus, amely valaminek cı́mét tartalmazza. A legegy-
szerűbben az alaptı́pusok mutatóit értelmezhetjük.

Vegyük példának egy egész tı́pusú változó pointerét. A deklarációja a következő:

int *ip;

Láthatjuk, hogy meg kell adni a tı́pust, majd a csillag jelzi, hogy ez nem egy változó, hanem egy pointer,
aztán jön a pointer neve.

Hogyan kap értéket egy ilyen pointer? Nézzük a következő program részletet!

int *ip;
int i;
:
:
ip=&i;

Láthatjuk, hogy van egy *ip pointerünk és van egy i változónk aztán az ip=&i; sorban az ip értéket
kap a & operátor segı́tségével. Ez a cı́mképző operátor.

Szó szerint az adott sor azt jelenti, hogy:

Az ip érteke legyen egyenlő i cı́mével.

Ez az operátor nem keverendő a bitenkénti és operátorával. A cı́mképző operátor egyoperandusú, mı́g a
bitenkénti és kétoperandusú.

Bármelyik alap- és definiált tı́pusnak van mutatója, de a mutatóknak is lehet mutatója. Például egy egész
mutató mutató deklarációja a következő:

int **p;
25
Ezt most nagyon nehezen ı́rtam le. Ezen a helyen az objektum kifejezés zavart kelthet. Az objektumokról a ??. oldalon
beszélünk.

39
A p természetesen csak a név26 .

2.8.2. Indirekció

Az indirekció egy igen fontos fejezete a C nyelvnek. Ezt rögtön egy programrészleten mutatjuk be.

1 int i,j;
2 int *ip;

3 i=5;
4 ip=&i;
5 j=*ip;

Az 1. sorban két int változót deklaráltunk és a másodikban egy int pointert.

A 3. sorban az i változó értéket kapott.

A 4. sorban az ip pointer értéke az i változó cı́me lett.

Az 5. sor egy úgynevezett indirekt értekeadás A 5. sor szó szerint a következőt csinálja:

A j változó értéke legyen egyenlő az ip pointer által mutatott változó értékével.

Mivel az ip ebben az esetben az i cı́mét tartalmazza j értéke i értékével lesz egyenlő. De nem közvet-
lenül, hanem a pointeren keresztül, ezért nevezzük ezt indirekt értékadásnak.

Persze fordı́tva is lehetséges, ha most az ı́rjuk, hogy:

*ip=9;

akkor az i változó értéke 9-re változik, mert - mint eddig azt láttuk - ip i cı́mét tartalmazza.

Az indirekció operátora a * operátor, amely nem tévesztendő össze a szorzás operátorával. Az indirekció
operátora egyoperandusú, mı́g a szorzás operátora kétoperandusú operátor.

2.8.3. Függvény mutatók

A függvény is egy objektum, meghatározott helye van a számı́tógép memóriájában, ezért létezik rá mu-
tató is.

Nézzünk egy példaprogramot a függvénymutató deklarálására és használatára.


26
Kipróbáltam meddig megy, de 15 csillagnál abba hagytam. Még egy megjegyzés háromszoros pointert már használtam.

40
1 #include <stdio.h>
2 int fgv(int);
3 int main(void)
4 {
5 int r;
6 int (*fgv_pt)(int);
7 fgv_pt=fgv;
8 r=fgv_pt(42);
9 printf("%i",r);
10 return 0;
11 }
12 int fgv(int x)
13 {
14 return x;
15 }

A 2. sorban deklarálunk egy függvényt, amelyet 12. sortól ı́runk meg. A függvény csak annyit csinál,
hogy az átadott paramétert visszaadja a hı́vó félnek.

Az 5. sorban deklarált változóban kapjuk meg az eredményt.

A 6. sorban egy függvény pointert deklarálunk. A pointer neve fgv_pt. Láthatóan van tı́pusa és
paraméter listája. Ez a pointer egy olyan függvényre fog mutatni, amely int tı́pusú és egyetlen int
paramétere van. Az fgv függvény pont ilyen27 .

A 7. sorban a pointernek átadjuk az fgv függvény cı́mét. Figyeljük meg, hogy a függvény cı́mét a
függvény neve képviseli a zárójelek nélkül.

A 8. sorban a pointeren keresztül - vagyis indirekt módon - meghı́vjuk a függvényt. A visszatérési értéket
az r változóban kapjuk meg. Az egész indirekt meghı́vás olyan, mintha a függvényt közvetlenül hı́vtuk
volna.

2.8.4. A void pointer

A void pointer egy általános pointer semmilyen tı́pushoz sem kötődik. Ezt a pointer tı́pust jó néhány
könyvtári függvény használja főleg azért, mert nem lehet tudni előre, hogy milyen tı́pusú adatokon fog a
függvény dolgozni.

A void pointer eltér a normál tı́pusokra vonatkozó pointerektől abban, hogy nem igaz rá a pointer
aritmetika.

A pointer aritmetikát a 44. oldalon ismertetjük.


27
Vajon miért?

41
2.9. Összetett adatszerkezetek

2.9.1. Tömbök

A tömb egy olyan összetett adatszerkezet, amely azonos tı́pusú adatokat rendez egy összefüggő memória
területen.

Egy egydimenziós int tömb deklarációja a következő módon történik:

int t[10];

Ez a tömb 10 elemű, tehát 10 darab int tı́pusú változót tartalmaz. A tömb elemeire a tömb neve és az
úgynevezett index segı́tségével hivatkozunk.

A példában egy tı́z elemű tömb van ennek indexelési tartománya 0..9. Tehát a tömb első elemének indexe
0, az utolsó elemének indexe 9.

Egy tömb elemre a következőképpen hivatkozhatunk:

t[5]=8;

Tehát a t[5] egy tömb elem, amelyet pontosan ugyanúgy használhatunk, mint bármilyen más int
tı́pusú változót.

Az index lehet pozitı́v egész szám, vagy egész jellegű változó.

Figyelem! A C nem ellenőrzi futásidőben az indexelési tartományt. Tehát könnyedén túlindexelhetünk.


Vagyis olyan index tartományt adunk meg, ami már nem a tömb területére hivatkozik.

A definı́cióban emlı́tettük, hogy a tömb összefüggő memóriaterületen foglal helyet. A legalacsonyabb


indexű elem a legalacsonyabb memória cı́men található, a következő elem közvetlenül mögötte és ı́gy
tovább a növekvő memória cı́mek felé.
A memória
eleje

0
1
2
A memória
vége

n-1

7. ábra: A tömb a memóriában

A tömb deklarációkor inicializálható28 .


28
Kezdő értéket lehet neki adni.

42
int t[5]={1,3,-2,9,123};

Ilyenkor nem szükséges megadni a tömb méretét.

int t[]={1,3,-2,9,123};

A tömb mérete ilyenkor is 5 lesz.

Ha megadjuk a tömb méretét, de több elemet akarunk a tömbbe beletenni, mint a méret, akkor fordı́táskor
hibaüzenetet kapunk. Ha kevesebbet a tömböt a fordı́tó program az elejétől kezdve feltölti, a hiányzó
elemeket általában 0-val tölti fel29 .

A tömb kezdőcı́me úgy határozható meg, hogy egyszerűen a tömb nevét használjuk hasonlóan a függvény
pointerekhez.

A következő program részlet erre mutat rá.

int t[10];
int *ip;
:
:
ip=t;

Az ip=t; kifejezés teljesen megegyezik a ip=&t[0]; kifejezéssel.

Erről még esik szó a pointer aritmetikánál.

2.9.2. Többdimenziós tömbök

A C - hasonlóan más nyelvekhez - ismeri a többdimenziós tömböket. A dimenziók számában nincs


korlátozás, azonban ne feledjük, hogy a lefoglalt memóriaterület mérete a dimenziók számával expo-
nenciálisan növekszik.

Deklaráljunk egy kétdimenziós int tömböt!

int t[3][2];

Az indexelési tartomány dimenziónként 0..n-1 tartományban van. Tehát a példa tömbünk első eleme
t[0][0], az utolsó eleme pedig a t[2][1]

Ekkor is a tömbelemek egymást követve helyezkednek el a memóriában úgy, hogy a leghátsó index
változik a leggyorsabban.
29
Ezt fordı́tó programja és a tömb helye határozza meg.

43
A memória
eleje
00
01
10
11

A memória
20

vége
21

8. ábra: A kétdimenziós tömb a memóriában

A többdimenziós tömbök is inicializálhatók deklarációkor (szépen).

int t[3][2]={ { 1,2 },


{ 3,4 },
{ 5,6 } };

(és nem szépen.)

int t[3][2]={1,2,3,4,5,6};

Az első módszer azért jobb, mert sokkal jobban követhető, ha bármilyen hibát vétünk az jól javı́tható. A
második módszer esetén egy elı́rás nagyon nehézzé teszi a javı́tást30 .

A tömb mérete lekérdezhető a sizeof operátor segı́tségével. Figyelem a tömb méretét a bájtban adja
meg a sizeof és csak abban a modulban, ahol a tömböt deklaráltuk.

2.9.3. Pointer aritmetika

Ennek a fejezetnek logikailag nem itt van a helye, de a pointer aritmetika működése sokkal könnyebben
követhető az egydimenziós tömbök segı́tségével, mint önállóan.

A pointereken öt művelet hajtható végre, ezek:

• pointer inkrementálása,

• pointer dekrementálása,

• egész szám hozzáadása a pointerhez,

• egész szám kivonása a pointerből,


30
Na ja nem 6db elem esetén

44
• két pointer kivonása egymásból.

A pointer inkrementálásánál a kiindulási memóriacı́m annyi bájttal növekszik, amekkora a pointer által
mutatott tı́pus mérete.

Ha tehát a kérdéses tı́pus char az inkrementálás során a cı́m a következő bájtra fog mutatni. Ha a
kérdéses tı́pus double akkor cı́m nyolc bájttal fog nőni.

A pointer dekrementálásakor a cı́m csökken a tı́pusnak megfelelő mérettel (bájtban értelmezve).

Nézzük a következő programrészletet:

double t[10];
double *p;
:
p=t;
p++;

A példában double tı́pusú tömb kezdőcı́mét átadjuk a pointernek, vagyis az a 0-ás indexű elem cı́mét
tartalmazza. Az inkrementálás után a pointer az 1-es indexű elemre fog mutatni.

Nagyon jól kihasználható tulajdonság, az hogy a tömbök és a pointerek ”átjárhatók”. Nézzük a következő
példát.

double t[10];
double *p;
:
p=t;
p[2]=3.1415;

A pointer megkapja a tömb kezdőcı́mét, ezután a pointert használjuk úgy, mint a tömböt. Vagyis a
p[2]=3.1415; kifejezés valójában a t tömb 2-es indexű elemébe helyezi el a PI közelı́tő értékét.

Ez a gondolat rögtön átvezet a pointerek értékének egész számmal történő módosı́tására.

Ha egy pointer értékét egész számmal módosı́tjuk (hozzáadunk, kivonunk), akkor a pointer értéke annyi
bájtszor tı́pusmérettel módosul, amekkora a kérdéses egész szám mérete.

A következő programrészlet ezt mutatja be.

double t[10];
double *p;

45
:
p=t;
*(p+2)=3.1415;

Ekkor a *(p+2)=3.1415; kifejezés megint a t[t] tömb 2. indexű elemébe teszi a PI értékét.

Két pointer kivonása esetén az adott tı́pus méretében megkapjuk a két pointer távolságának egész részét.
Tehát:

double t[10];
double *p1,*p2;
int i;
:
p1=\&t[2];
p2=\&t[4];
i=p2-p1;

Az i értéke ebben az esetben 2.

2.9.4. Sztringek

Más programozási nyelvekben járatos olvasó esetleg hiányolhatta a tı́pusok ismertetésénél a sztringek
hiányát. A C nem tekinti tı́pusnak a sztringeket, hanem karakter tömbként kezeli őket.

A sztring végét egy 0 értékű31 karakter jelzi, függetlenül a tömb méretétől.

A sztring deklarálásnál inicializálható, hasonlóan más változókhoz és tömbökhöz. A következő példa
ezt mutatja be.

char nev[]="Nokedli";

A nev nevű tömb 8 elemű, mert ebben az esetben a fordı́tó program gondoskodik a lezáró 0-ról. Tehát
a 7-es indexű elem értéke 0.

Természetesen deklaráláskor itt is megadhatjuk a tömb méretét.

A sztingekre való hivatkozáskor nem a sztringre, mint értékre hivatkozunk, hanem a sztringet tartalmazó
tömb kezdőcı́mével, tehát a következő programrészlet teljesen rossz!

char a[]="Hello!";
char b[80];
31
És nem kódú.

46
b=a;

Figyelem az előző programrészlet HIBÁS!

Az ilyen problémák megoldására a string.h header fájlban deklarált függvények szolgálnak.

2.9.5. Struktúrák

A tömbök szigorúan csak azonos tı́pusú változók tárolására szolgálnak. Számos esetben azonban szükségünk
lehet logikailag egybe tartozó, de különböző tı́pusú adatok kezelésére. Erre szolgálnak a C-ben a struktúrák.

A struktúra bármilyen tı́pusú adatelemet, tömböt, más struktúrát és tetszőleges pointert tartalmazhat
kivéve pontosan olyan struktúrát mint önmaga. Ezt a megszorı́tás igaz közvetlen és közvetett tartal-
mazásra is32 .

Ha struktúrát szeretnénk alkalmazni, először azt definiálni kell. Ez esetleg lehet gyári definı́ció, ekkor
általában egy header fájlban ezt a rendszer gyártója megteszi. Ha saját struktúrát készı́tünk akkor nekünk
kell a definı́ciót elkészı́teni.

Ha a definı́ció kész, utána készı́thetünk belőle példányt. Ekkor tulajdonképpen egy adott struktúra
”tı́pusú33 ” változót hozunk létre.

A következő példa a struktúrák használatát mutatja be.

1 #include <stdio.h>
2 #include <string.h>
3 struct ADAT
4 {
5 char nev[80];
6 unsigned char kor;
7 double suly;
8 long long sszam;
9 };
10
11 int main(void)
12 {
13 struct ADAT ember;
14 strcpy(ember.nev,"Tojas Tobias");
32
Tehát ha ez tartalmaz egy olyan struktúrát, amely valahol az ”alap” struktúra példányát tartalmazza, esetleg több áttétellel
is.
33
Ne igazán tudok jobb kifejezést erre, mint a tı́pus, mert tulajdonképpen ez az, de a későbbiekben látni fogjuk, hogy vannak
tı́pusként definiált struktúrák is.

47
15 ember.kor=22;
16 ember.suly=72.93;
17 ember.sszam=99813342134;
18 return 0;
19 }

A program semmi mást nem csinál, mint definiál egy struktúrát, majd létrehoz egy adott struktúra
példányt és azt feltölti adatokkal.

Nézzük részletesen!

a 3.-tól a 9. sorig láthatjuk az ADAT ”tı́pusú” struktúrát. A 3. sorban adjuk meg a struktúra ”tı́pus” nevét
a struct kulcsszó után.

A 4. sorban láthatjuk a struktúra belsejét megnyitó kapcsos zárójelet.

Az 5. sortól deklaráljuk a struktúra mezőit. Jelen esetben ezek a mezők egy 80 karakteres tömb, egy
előjeltelen karakter, egy dupla pontosságú lebegőpontos és egy long long tı́pusú változók.

A definı́ciós részt a záró zárójel és a pontosvessző zárja le34 .

A 13. sorban létrehozunk egy példányt az ADAT ”tı́pusú” struktúrából.

A 14. sort picit tegyük félre.

A 15. sorban az ember struktúra kor mezőjét töltjük fel. Ekkor a ’.’ mezőhozzáférés, vagy pont
operátort használjuk. A feltöltéshez tehát a ember.kor=22; kifejezést alkalmazzuk, ami szó szerint
azt jelenti, hogy az ember struktúra kor mezője legyen egyenlő 22-vel.

Egy struktúra egy adott mezője ugyanúgy kezelhető, mint egy a mezővel azonos tı́pusú változó. Tehát
lehet neki értéket adni és az értékét felhasználni.

A 16. és 17. sorban a többi mezőt töltjük fel.

A 14. sorban egy függvényt kell használnunk, mert a sztringeknek nem lehet közvetlenül értéket adni.
A sztringre való hivatkozás nem a sztring értékével, hanem cı́mével történik. A strcpy függvény az
argumentumának első tagjában megadott cı́mre másolja a második argumentumban megadott sztringet.
Vagyis feltölti a nev mezőt.

Ezután a struktúra mezőit pontosan ugyanúgy használhatjuk, mint a ”közönséges” változókat.

A struktúra inicializálható a példány deklarálásánál, hasonlóan a tömbökhöz Ez a példánkban a 13. sor-


ban lehetséges a
34
A definı́ciós részt, vagy más néven struktúra definı́ciót úgy tekinthetjük, mint az ilyen ”tı́pusba” tartozó struktúrák terv-
rajzát.

48
13 ADAT ember={"Tojas Tobias",22,72.99,99813342134};

módon. Ne feledjük az ilyen jellegű feltöltés csak itt a deklarációnál lehetséges.

Mi történik akkor, ha a struktúra cı́me áll rendelkezésre és nem a példány. A 14. sortól szúrjuk be a
következő sorokat:

14 ADAT *ept;
15 ept=&ember;

Tehát az ept egy olyan pointer, amely az ember struktúra példányra mutat.

Ekkor használhatjuk az úgynevezett -> operátort. Ekkor egy mező elérése az alábbi módon történhet:

ept->kor=22;

Ez a sor szó szerint a következőt teszi:

Az ept mutató által cı́mzett struktúra kor mezője legyen 22.

A következő kérdés a struktúra mérete. Első közelı́tésben azt mondhatnánk, hogy a struktúra akkora
területet foglal le, mint a benne lévő mezők méretének összege. Ez az állı́tás nem biztos, hogy igaz,
mert a különböző processzorok és környezetek az adatok tárbeli elhelyezkedését optimalizálják adott
szempontok szerint és ı́gy próbálják a program futását gyorsı́tani.

Ezért a struktúra vagy egy struktúra példány méretét célszerűen a sizeof operátorral kell meghatározni.
Vagyis jelen esetben a

s=sizeof(ADAT);, vagy s=sizeof(ember);

kifejezés használata vezet helyes eredményre.

2.9.6. Tı́pusként definiált struktúra

A struktúrák definiálhatók tı́pusként is a typedef kulcsszó segı́tségével. Nézzük erre az előző példát!

1 #include <stdio.h>
2 #include <string.h>
3 typedef struct
4 {
5 char nev[80];
6 unsigned char kor;
7 double suly;

49
8 long long sszam;
9 } ADAT;
10
11 int main(void)
12 {
13 ADAT ember;
14 strcpy(ember.nev,"Tojas Tobias");
15 ember.kor=22;
16 ember.suly=72.93;
17 ember.sszam=99813342134;
18 return 0;
19 }

Láthatjuk, hogy a struktúra felépı́tése nem változott meg, de a tı́pus azonosı́tója az ADAT a definı́ció
végére került.

Egy másik eltérés, hogy a 13. sorban lévő deklaráció egyszerűbb (és szebb) lett35 .

A példányt ezután teljesen úgy kezeljük, mint az előzőekben látott ”hagyományos” definı́cióval megadott
struktúrát36 .

2.9.7. Bitmező struktúrák

A bitmező struktúrák lehetővé teszik, hogy egész jellegű mezőkből úgynevezett bitmezőt hozzunk létre.

A bitmezőt olymódon kell elképzelni, hogy a kı́vánt (és szigorúan egész jellegű) tı́pusú változóból hány
bites mezőt szeretnénk létrehozni. A mező hossza nem lehet nagyobb, mint az eredeti tı́pus hossza37

Egy bitmező struktúra definı́ciója a következő:

1 struct bitfield
2 {
3 unsigned short a: 3;
4 unsigned short b: 8;
5 unsigned short c: 4;
6 unsigned short d: 1;
7 };
35
Azt, hogy a programozó melyik módszert használja, az az ő egyéni ı́zlésétől függ.
36
Van egy apró eltérés ezt nemsokára a rekurzı́v struktúráknál látjuk majd.
37
Valamikor ezt a megoldást arra találták ki, hogy memória helyet takarı́tsanak meg. Manapság a PC-k világában ez nem
ennyire éles, hiszen a memória - akár fizikailag és átvitt értelemben is - nagyon olcsó. A bitmezőket inkább a mikrokontrollerek
esetén használjuk egyrészt valóban helytakarékossági szempontból, másrészt a hardver programozásához.

50
A 3. sorban létrehozunk egy 3 bit szélességű mezőt. A 4. sorban egy 8-bites, az ötödik sorban egy
4-bites és a 6. sorban egy egybites mezőt.

Ha ezt most összeadjuk, akkor pontosan 16 bitet kapunk, ami az unsigned short tı́pus mérete. Tehát
egy ilyen struktúra két bájtot foglal le a memóriában.

Nem kötelező a mezőknek kiadni az alaptı́pus méretét, lehet ez rövidebb, vagy hosszabb.

Ha mondjuk a b mező most csak 6 bites lenne, akkor az ilyen ”tı́pusú” struktúra még mindig két bájtot
foglalna le.

Most feltételezzük azt, hogy a d mező 2 bit méretű lenne. Ekkor a bitmezők szélességének összege 17
lenne, ami nagyobb, mint az unsigned short mérete. Ha most deklarálunk egy ilyen elemet, akkor
annak mérete 4 bájt lenne, mert a fordı́tó ”megkezdett” egy új unsigned short-ot.

Az ilyen struktúrák mezőt pontosan ugyanúgy lehet elérni, mint egy hagyományos struktúráét, csak
nagyon figyelni kell a számábrázolási tartományra.

Nézzük példának a következő esetet:

int i: 1;

Kérdés: mi az i mező számábrázolási tartománya. A válasz:{0,-1}. A válasz indoklását az olvasóra


bı́zzuk.

A bitmezők keverhetők a hagyományos struktúra elemekkel is. Lásd a következő példát:

1 struct mix
2 {
3 unsigned char a: 3;
4 unsigned char b: 5;
5 float f;
6 };

A példa 5. sorában lévő float tı́pusú elem egy ”normál” struktúra mező.

Megjegyzés: lehet, hogy mikrokontrolleres esetben helyet takarı́tunk meg a bitmezők alkalmazásával,
azonban amikor ezeket az elemeket használjuk a processzornak az adatokat tologatnia kell. Ez bizony
időbe kerül.

A bitmező struktúrák tı́pusként is definiálhatók.

51
2.9.8. Rekurzı́v struktúrák

Az előzőekben láthattuk, hogy egy struktúra bármilyen elemet tartalmazhat, csak olyan struktúrát sem-
miképpen sem, mint amilyen önmaga.

Viszont tartalmazhat olyan tı́pusú pointert, mint önmaga. Nézzünk erre egy példát:

1 struct ITEM
2 {
3 int v;
4 struct ITEM *next;
5 };

Ez a struktúra tehát két mezőt tartalmaz a 3. sorban lévő v nevű egészt és egy olyan tı́pusú pointert, mint
önmaga.

Megjegyzés: ez a struktúra az úgynevezett lista adatszerkezet egy elemét ı́rja le. A lista adatszerkezet
felépı́tése olyan, hogy van egy adatrésze (nem okvetlenül egy elemű), és van egy mutatója, amely a
következő elemre mutat.

A lista csak elölről olvasható, visszafelé nem és végét a C nyelvben úgy jelezzük, hogy az utolsó elem
next pointere egy úgynevezett NULL pointer38 .

A rekurzı́v struktúrák nem definiálhatók tı́pusként, ennek oka, hogy a tı́pus a definı́ció végén derül ki.
Így fordı́tás során a fordı́tó program a mutató tı́pusát nem fogja felismerni.

2.10. Unionok

A union az egyik legfurcsább szintaktikai elem. Formailag nagyon hasonlóak a struktúrához, de teljesen
más célra használjuk.

Mı́g a struktúra arra szolgál, hogy különböző elemeket egy egységként tudjunk kezelni, a union célja az,
hogy egy adott memória területet különböző tı́pusként érhessünk el. Nézzünk erre egy példát!

A union definı́ciója:

1 union SAMPLE
2 {
3 float a;
4 int b;
5 };
38
A NULL pointert például ı́gy lehet előállı́tani: #define NULL ((void *)0)

52
Mint látjuk a union definı́ciója nagyon hasonlı́t egy struktúra definı́ciójához.

A union tı́pusa jelen esetben SAMPLE , lásd 1. sor.

A 3. sorban egy float mezőt definiálunk a néven.

a 4. sorban egy int tı́pusú mező definiálása látható.

A definı́ció után deklaráljunk egy union SAMPLE tı́pusú változót.

union SAMPLE minta;

Ekkor már van egy valós változónk. Ha ez struktúra lenne, akkor a minta mérete legalább akkora lenne,
mint a float és az int változó méretének összege39 .

Azonban jelen esetben a sizeof(minta) csak 4-es értéket ad. A union két mezője egyazon területet
definiál. Ha a program során az ı́rjuk, hogy

... minta.a ... ,

akkor az adott memória területhez float-ként férünk hozzá.

A következő kifejezés esetén

... minta.b ... ,

a minta változó területét int tı́pusként használjuk40 .

..
.m ..
in b.
fl ta. ta
.
oa a. in t
t .. m
. in
..

minta

9. ábra: A minta példa grafikusan

Sokkal életszerűbb az a megvalósı́tás, amikor egy union-t arra használunk fel, hogy egy nagyméretű
változót bájtokra bontsunk adott cél érdekében.

Ez az előző float estén ı́gy nézhet ki:


39
Ez a gcc esetén 4+4 bájt, vagyis 8 bájt lenne.
40
Ez a példa igazából nem túl értelmes, csak a union használatát mutatja be

53
union convert
{
float f;
char b[4];
};

Mint láthatjuk a unionba f mezőként elhelyezett float tı́pusú változó bájtokra bontva a b tömbben
kapható meg.

Felmerül a kérdés, hogy mi történik abban az esetben, ha a két, vagy több mező mérete nem azonos.
Ekkor:

• a legnagyobb mező határozza meg a union méretét,

• a mezőket a union kezdőcı́métől számı́tjuk.

Definiáljunk egy ilyen uniont!

union SAMPLE
{
float f;
short s;
char c;
};

Majd deklaráljunk egy példányt belőle!

union SAMPLE pld;

A következő ábra azt mutatja, ahogyan a különböző mezők ”látják” a memóriát.

54
float f;

short s;

char c;
&pld

Erre nő a memóri cím

union SAMPLE pld;

10. ábra: A eltérő méretű mezők helye a unionban

2.10.1. Tı́pusként definiált unionok

A unionok is definiálhatók tı́pusként. Nézzük példaként a SAMPLE union definiálását és egy példány
deklarálását ilyen módon!

typedef union
{
float a;
int b;
} SAMPLE;

és

SAMPLE minta

Láthatjuk, hogy ez is hasonlóan struktúrákhoz kicsit egyszerűbb lett. A mezők hozzáférése teljesen
azonos, mint a hagyományos definı́ció esetén

2.11. Modulok, moduláris programozás

A c programozási nyelvet eredetileg azzal a céllal hozták létre, hogy operációs rendszereket fejlessze-
nek benne. Azonban egy operációs rendszer forrása több 10, esetleg 100 ezer programsorból áll. Ezt
nyilvánvalóan nem lehet egyetlen forrásban tárolni, mert akkor ez fájl kezelhetetlen lenne. Ez ért a
programot külön forrás részletekre bontjuk. Ezeket a különálló programrészeket nevezzük modulnak.

FIGYELEM! A modul nem tévesztendő össze az include fájllal. A modul önállóan fordul forrásállományból
az úgynevezett object állományba Lásd 6. ábra!

55
A modulok lehetővé tették azt, hogy a programozó jól definiált és alaposan letesztelt előzőleg már megı́rt
program részleteket használjon.

Mivel várhatóan egy nagyobb programot több programozó készı́t, gondoskodni kell arról, hogy a változó
és kódnevek fölöslegesen ne keveredjenek. A modulok ennek a problémának a megoldását is lehetővé
teszik. Lehetőség van egyes függvények és változók elrejtésére.

Objektumok

Terület- Kód-
foglaló generáló

Globális Modulra Blokkra Modulra


Globális
lokális lokális lokális

Statikus Statikus Statikus Dinamikus

11. ábra: A C objektumai

A 11. ábra a C nyelv objektumait ábrázolja.

Definı́ció: a terültefoglaló objektumok a változó jellegű objetumok, úgymint:


változók,
• mutatók,
• tömbök,
• struktúrák,
• unionok.

Definı́ció: a kódgeneráló objektumok a függvények.

Definı́ció: egy változó statikus, ha a változó területe fordı́táskor jön létre. Amennyiben egy statikus
változót a deklarációnál inicializálunk az a program során egyszer és csakis egyszer kap kezdeti
értéket41 .

MEGJEGYZÉS: természetesen a változó értéket kaphat. Ez csak a kezdeti inicializálásra vonatkozik.

Definı́ció: egy változó dinamikus, ha a változó területe futásidőben jön létre és futásidőben is szűnik
meg.
41
Az esetlegesen még nem definiált fogalmakat ezen szakasz után tisztázzuk.

56
az fgv1 függvényt (7. sor), amely a 2.c modulban található.
Az program az 1.c modulban található main függvény meghı́vásával indul. A main először meghı́vja

a 2.c és a 3.c források.


A következő példa segı́t a további fogalmak megértésében. A program három modulból áll, ezek az 1.c,

ahányszor a változó létrejön.


Amennyiben egy dinamikus változót a deklarációnál inicializálunk az annyiszor kap kezdeti értéket,
1 #include <stdio.h> 1 #include <stdio.h> 1 #include <stdio.h>
2 void fgv1(void); 2 void fgv1(void); 2 void fgv2(void);
3 void fgv2(void); 3 static void fgv2(void); 3 void fgv3(void);
4 char txt[]="G text\n"; 4 extern char txt[]; 4 static char txt[]="N text\n";
5 int main(void) 5 void fgv1(void) 5 void fgv2(void)
6 { 6 { 6 {
7 fgv1(); 7 printf("%s",txt); 7 printf("G fgv2\n");
8 fgv2(); 8 fgv2(); 8 printf("%s",text);
57

9 return 0; 9 } 9 fgv3();
10 } 10 void fgv2(void) 10 }
11 { 11 void fgv3(void)
12 printf("ML fgv2\n"); 12 {
13 } 13 char txt[]="B text\n";
14 printf("%s",txt);
15 }

1.c 2.c 3.c


Figyeljük meg, hogy az 1.c modulban az fgv1 deklarálásra került (2. sor).

A 2.c modulban található fgv1 függvény definı́ciója (5-9. sor). Ez először kiı́rja a txt nevű sztringet.
Ez a sztring globális váltózóként került deklarálva az 1.c modulban (4. sor).

Azonban ezt a sztringet deklarálni kell abban a modulban is, ahol használni szeretnénk. Tehát egy dek-
larációra a 2.c modulban is szükség van, különben a fordı́tó olyan hibajelzést küld, hogy nem deklarált
változót használunk. Ez a deklaráció viszont nem történhet ugyan úgy, mint azt az 1.c-ben láttuk, mert
ez egy új változó terület lefoglalását jelentené.

Ezért a txt deklarációja a 2.c modulban egy extern előtaggal történik. Ez azt mondja a fordı́tó
programnak, hogy van már ilyen nevű változó terület deklarálva, de nem az aktuális modulban, hanem
valahol egy másikban.

Ezután a 2.c-ben az fgv1 meghı́vja a 2.c-ben található fgv2 függvényt(10. sor). Az ért ezt hı́vja meg
és nem a 3.c-ben lévőt, mert a 2.c-ben lévő fgv2 függvény static előtaggal deklaráltuk. Ezáltal
ez a függvény modulra lokális lett.

Ezután a program visszatér az 1.c modulba, ahonnan meghı́vja a 3.c modulban található fgv2 függvényt.

A 3.c modulban az fgv2 függvény kiı́rja, hogy G fgv2 és a txt változót. Azonban ez a txt az
adott modulban egy static előtaggal rendelkezik (4. sor). Ez azt jelenti, hogy ez egy modulra lokális
változó. és a globális txt sztringtől különböző változó területet foglal le.

Ezután a 3.c, fgv2 meghı́vja az fgv3-t, amely kiı́r újra egy txt sztringet, amely viszont az fgv3-ban
lett deklarálva (12. sor). Ekkor ez a txt egy blokkra lokális (és dinamikus) változó.

1.c 2.c 3.c Képernyő

main
fgv1
G text
fgv2
ML fgv2

fgv2
G fgv2
N text
fgv3
B text

Program
vége

12. ábra: A program működése

Definı́ció: Egy változó globális, ha minden modulból megvan a lehetősége, hogy elérhető legyen.

58
• ezt a változót a teljes program folyamán egyszer és csakis egyszer egy modulban, minden függvényen
kı́vül deklarálni kell úgy, hogy a deklaráció nem kap semmilyen előtagot. Ekkor ebben a modulban
történik meg a változó helyének lefoglalása.

• amennyiben a kérdéses változót egy másik modulból el akarjuk érni, akkor az adott modulban
a változó deklarációja elé egy extern kulcsszót kell tenni. Ekkor új memória lefoglalás nem
történik.

• ezt a változót csak abban a modulban lehet deklarációkor kezdőértékkel ellátni, amelyben a változó
terület lefoglalásra kerül42 .
a globális változók statikusak.

Definı́ció: egy változó modulra lokális, ha a kérdéses változó a modul összes függvényéből elérhető
(kivételt lásd lejjebb), de más modulokból ez a változó nem látható.

• a modulra lokális változók deklarációja esetén a deklaráció kap egy static előtagot,

• a modulra lokális változók statikusak.

Definı́ció: egy változó blokkra lokális, ha érvényességi köre csak egy függvényre korlátozódik,

• a blokkra lokális változók deklarációja az adott függvény törzsének az elején található,

• a blokkra lokális változók lehetnek dinamikusak és statikusak.

Felmerül a kérdés, hogy mi történik akkor, ha azonos néven létezik egy globális, modulra lokális és egy
blokkra lokális változó. Melyiket látja a kérdéses függvény.

A válasz az, hogy mindig azt a változót látjuk, amely közelebb van. Tehát sorrendben a blokkra lokális
a modulra lokális és a globális a láthatósági sorrend43

Definı́ció: egy függvény globális, ha meg van annak a lehetősége, hogy tetszőleges modulból elérhető
legyen.

• a globális függvényt célszerű mindazon modulokban deklarálni, ahol használni szeretnénk.

• a globális függvényt egy és csakis egy modulban szabad és kell is definiálni,

• a globális függvény deklarációjánál semmilyen előtagot sem alkalmazunk.


42
Tehát az extern int a=5 Nem helyes!
43
Persze. Miért is hoznánk létre olyan változót, amit nem látunk.

59
Definı́ció: egy függvény modulra lokális, ha az az adott modul minden függvényéből meghı́vható, de
más modulokból nem.

• a modulra lokális függvény deklarációja elé egy static kulcsszót kell tenni,

• a modulra lokális függvényt az adott modulban definiálni kell.

Abban az esetben, ha egy modulban szerepel egy olyan nevű modulra lokális függvény, amely néven a
programban azonos néven globális függvény is szerepel, akkor a modul függvényeiből csak a modulra
lokális érhető el44 .

Lehetőség van blokkra lokális statikus változó deklarálására is. Ekkor a következő módon kell a
változót deklarálni az adott függvényben:

void fgv(void)
{
static int i;
:
:

Vagyis a változót ugyanott a függvényen belül deklaráljuk, de elé tesszük a static kulcsszót. Ekkor a
példában szereplő i változó statikus lesz, tehát fordı́tás időben jön létre.

Fontos!! Ha egy blokkra lokális statikus változót hozunk létre, akkor ez a változó épp úgy, mint a blokkra
lokális dinamikus változó csak és kizárólag az adott függvényben látható. De a statikus változó egyszer
és csakis egyszer jön létre - mégpedig fordı́tás időben - akárhányszor is hı́vjuk meg a függvényt.

A blokkra lokális dinamikus változók esetén a változó terület mindig újra létrejön és megsemmisül a
hı́vások és kilépések során.

A fentieknek két következménye van:

• a blokkra lokális statikus változó nem veszti el az értékét két függvényhı́vás között, mı́g a blokkra
lokális dinamikus igen,

• a blokkra lokális statikus változó deklarációkor történő értékadása csak egyszer és csakis egyszer
történik meg, mı́g a blokkra lokális dinamikus változónál ez a művelet minden függvényhı́vásnál
végrehajtódik.

void fgv(void) void fgv(void)


44
Ez van közelebb.

60
{ {
static int i=5; int i=5;
: :
: :

Statikus eset Dinamikus eset

A változók működésének bemutatására ı́rjunk egy olyan rekurzı́v faktoriális számoló függvényt, amely
kiı́rja, hogy mekkora a rekurzió mélysége45 .

Definı́ció: rekurzı́vnak nevezzük azt a függvényt, amely önmagát hı́vja meg.

1 unsigned fact(unsigned n)
2 {
3 static int cnt=0; // Ez a melyseg szamlalo
4 unsigned r; // Seged valtozo
5 if(n<=1)
6 {
7 cnt++;
8 printf("%i\n",cnt);
9 return 1;
10 }
11 cnt++;
12 r=n*fact(n-1); // A tenyleges rekurziv hivas
13 return r;
14 }

A 3. sorban a static int cnt=0; kifejezés egy blokkra lokális statikus változót hoz létre, amelyet
0 értékre inicializál Ez egyszer történik meg a program során.

A 4. sorban az r változó egy blokkra lokális dinamikus változó.

Az 5. sorban megvizsgáljuk, hogy az n változó értéke 1, vagy esetleg 0. Ha igen, akkor a függvény 1
értekkel tér vissza.

Ha n értéke 1-nél nagyobb, akkor a függvény inkrementálja a cnt változót és a 12. sorban meghı́vja
önmagát eggyel csökkentett értékkel. A függvény visszatérési értékét az n értékkel megszorozzuk.

Ez mindaddig ı́gy megy, amı́g az átadott paraméter értéke nem lesz 1. Ekkor a rekurzı́van meghı́vott
függvények elkezdenek visszatérni.

Tegyük fel, hogy 3! szeretnénk kiszámolni46 .


45
Azn faktoriálisa n! a következő: n! = 1 ∗ 2 ∗ ∗ n definı́ció szerűen: 0! = 1, 1! = 1, , 2! = 2 ∗ 1, , n! = n ∗ (n − 1)!.
46
Ez nem programlista, hanem egyfajta működési leı́rás.

61
Láthatjuk, hogy a cnt változó területe minden hı́vásnál ugyanaz. Az r változó viszont minden hı́vásnál
újra létrejön.

Ez az r itt

Ez az r itt

Ez az r itt
jön létre

jön létre

jön létre
3
fact
r
2
fact
r
1
fact
r
1
2
6

cnt

A cnt fordításkor jött létre

13. ábra: A változók a rekurzı́v függvényhı́vásban

A jobbra mutató nyilakon lévő számok az átadott paraméter értéket, a balra mutató nyilakon lévő számok
a függvény aktuális hı́vásából származó visszaadott értéket mutatják.

A vastag szürke vonalak közötti sáv azt mutatja, hogy a függvény belső, aktuális r - tehát az aktuális
függvényhı́váskori - változója mikor és hol látszik a program futása során.

62
3. I/O kezelés

Az I/O kezelés a UNIX rendszerekben alapvetően fájl kezelést jelent. Mivel a C és a UNIX mondhatni
szimbiózisban van, ezért mi is ezt a fogalomkört használjuk.

A UNIX jellegű rendszerekben bármilyen eszközt szeretnénk elérni, azt fájl elérésként tehetjük meg47 .

Ebbe most részletesebben nem megyünk bele, mert a jegyzet tárgya nem a UNIX jellegű rendszerek
kezelése, hanem a C programozási nyelv.

3.1. Standard I/O

A UNIX jellegű rendszerek három standard fájlt használnak, ezek:

• standard input, amely általában a billentyűzet (stdin),

• standard output, amely általában a képernyő (stdout),

• standard error, amely általában szintén a képernyő (stderr).

Az stdin-t és az stdout-ot speciálisan erre a célra ı́rt függvényekkel kezelhetjük. Az stderr ke-
zelésére a később ismertetésre kerülő fájlkezelő függvények szolgálnak.

3.1.1. printf

A printf függvény az stdout-ra ı́r ki formátumozottan. Deklarációja a stdio.h header fájlban


található és egyszerűsı́tett formában a következő:

int printf(const char *format, ... );

A függvény első paramétere az úgynevezett formátum sztring . Ebben adjuk meg azt, hogy a következő
paraméter listát a printf hogyan értelmezze.

A formátum sztring tartalmazhat formátum specifikátorokat és egyéb szövegelemeket. A formátum spe-
cifikátort a függvény értelmezi. Azokat a részeket, amelyeket nem lehet specifikátorként értelmezni a
printf kiı́rja.

A következő argumentum a deklarációban a .... Ez azt jelenti, hogy a függvény ezután tetszőleges
számú és tı́pusú argumentum elemet tartalmazhat. Ezek értelmezése a kiı́ratásnál a formátum sztring
alapján történik.

A formátum specifikátor szerkezete:


47
Bármilyen ...X UNIX, Linux, QNX, stb.

63
%[flags][width][.prec][length]type

A nem kötelező elemeket [ ] pár közé tettük48 .

Mint láthatjuk a formátum specifikátor minden esetben egy % jellel kezdődik. És a tı́pus (type) kötelező,
a többi megadható paraméter opcionális.

Elsőnek nézzük a legfontosabb tı́pusokat!

d,i decimális egész kiı́ratás előjellel,

u decimális előjeltelen egész kiı́ratás,

o oktális előjeltelen egész kiı́ratás,

x,X hexadecimális előjeltelen egész kiı́ratás,

f,F egyszeres pontosságú lebegőpontos kiı́ratás 123.456 formában,

e,E egyszeres pontosságú lebegőpontos kiı́ratás 1.23456e2 , vagy 1.23456E2 formában,

g,G egyszeres pontosságú lebegőpontos kiı́ratás az előző két forma közül abban, amelyik a rövidebb49 ,

c egy karakter kiı́ratása,

s az argumentumban megadott cı́mű sztring kiı́ratása a lezáró nulla értékű karakterig,

p a megadott mutató értékét ı́rja ki hexadecimális formában.

Tehát, ha például egy karaktert akarunk kiı́ratni, akkor ez a következő módon történik:

printf("%c",chr);

ahol a karakter változó neve chr.

A flag karakterek a következők:

# tı́pustól és formátumtól függő speciális kiı́ratást ı́r elő:

o az oktális szám elé kiı́rja a 0 karaktert,


x, X a hexadecimális szám elé kiı́rja a x, vagy X karaktert,
f, F, e, E, g, G esetén mindenképpen lesz tizedesjegy.

más esetekben ez a flag hatástalan.


48
Csak a leı́rásban, amikor alkalmazzuk ezeket, akkor nem kell a [ ] pár.
49
Első pillantásra az f,F a rövidebb, de ha a 0.0000000001 számot vesszük, akkor az e,E a rövidebb (1e-10).

64
0 a kiı́ratási méretnek megfelelően a kiı́randó számokat feltölti 0 -kal. Az egészeket a bal oldalról a
lebegő pontosakat a jobb oldalról,

szóköz pozitı́v számok elé egy szóközt szúr be,

- a kiirtást balra rendezi. Az alapértelmezett a jobbra rendezés.

+ a pozitı́v számok esetén a kiı́rásnál egy + jelet tesz a szám elé.

A width mező megadja a minimális kiı́ratás szélességét. Ha a kiı́ratás valamilyen okból nagyobb, mint
amit előı́rtunk, akkor a kiı́ratás felülı́rja ezt az értéket.

A .prec mező hatása szintén tı́pusfüggő:

f, F, e, E, g, G esetén a kiı́randó tizedesjegyek számát adja meg50 ,

d, i, u, o, x, X a kiı́randó számjegyek számát adja meg úgy, hogy balról a számot nullákkal
tölti fel,

s, S a kiı́randó sztring maximális hosszát adja meg.

A length mező a kiı́ratás pontosságát határozza meg:

h előjeles, vagy előjeltelen egészek esetén short értéket ı́r ki,

hh előjeles, vagy előjeltelen egészek esetén egy bájtos (egész) értéket ı́r ki ( signed char , vagy unsigned
char ),

l előjeles, vagy előjeltelen egészek esetén long érteket ı́r ki,


lebegőpontos változok esetén double érteket ı́r ki,

ll előjeles, vagy előjeltelen egészek esetén long long értéket ı́r ki,

L lebegőpontos változok esetén ( f,F,e,E,g,G ) long double értéket ı́r ki.

A printf függvény egyszerre több értéket is kiı́rhat. A kiı́rás módját a formátum sztring határozza
meg. A formátum specifikátorok sorban meghatározzák a kiı́randó értékeket a következőképpen:

printf("%d%d%d",a,b,c);

A függvény először kiı́rja a, majd b és végül c értékét.

Természetesen a függvény egyszerre nem csak egyfajta tı́pust tud kiı́rni.


50
Lebegőpontos számok esetén a kiı́randó tizedesjegyek alapértelmezett száma hat. Természetesen ennél pontosabb értékkel
számol a gép.

65
Ha a formátum sztringben specifikátornak nem értelmezhető karakterek vannak, azokat a függvény az
adott helyen kiı́rja. Ahol formátum specifikátort talál, azon a pozı́ción a megfelelő változó értékét ı́rja
ki:

printf("a értéke=%i",a);

Ezt kiírja

Ide jön a értéke

Megjegyzés: a printf a formátum sztringben található formátum specifikátorból tudja, hogy az adott
változót hogyan kell kezelni, és nem törődik azzal, hogy az argumentumban mi a valójában a változó
tı́pusa.

Bár néhány okosabb fordı́tó esetleg figyelmeztetést küld (lásd gcc), de ezzel már futás időben nincs mit
tenni. Ekkor a kiı́ratás természetesen hibás lesz.

A fentiek alapján nézzünk néhány példát! Minden példa esetén a kiı́ratás a képernyő szélétől történik. A
szóközöket a  karakterek, a cursort a  karakter, a terminál szélét a k karakter jelzi. Legyen a kiı́randó
érték 123 és egész!

Legyen a formátum specifikátor: %5i

printf("%5i",123); k 123

Legyen a formátum specifikátor: %-5i

printf("%-5i",123); k123

Cseréljük ki a - flag-et + -ra!

printf("%+5i",123); k +123

Használjuk a szóköz flag -et!

printf("% i",123); k 123

Ugyanezt -123 -ra!

printf("% i",-123); k-123

Most használjuk a .prec mezőt!

printf("%5.4i",123); k 0123

66
Látható, hogy a szám elé egy 0 át odaı́r, mert a .prec egész jellegű változó esetén a minimálisan
kiı́randó számjegyek számát adja meg.

Legyen a formátum specifikátor újra: %5i

printf("%5i",123456789); k123456789

Látható, hogy a megadott szélességet, az 5-öt, a kiı́ratás szélessége meghaladta. Ekkor nem csonkul a
kiı́rás, a teljes számot kiı́rja.

Nézzünk példákat a lebegőpontos kiı́ratásra! A szám 123.456.

Legyen a formátum specifikátor: %f

printf("%f",123.456); k123.456

Cseréljük le az f -et e -re, majd E -re!

printf("%e",123.456); k1.23456e2

printf("%E",123.456); k1.23456E2

Legyen a formátum specifikátor: %.2f

printf("%.2f",123.456); k123.45

Láthatjuk, hogy a hatos már lemaradt, mert lebegőpontos számok esetén a .prec mező a kiı́randó
tizedesjegyek számát adja meg.

Használjuk a 0 flag-et!

printf("%0f",123.456); k123.456000

Ekkor a tizedesjegyek száma hat lesz, mert ez a default kiı́ratási méret. A program természetesen ennél
pontosabban számol, ez csak kiı́ratás.

Természetesen a kiı́ratási pontosság felülı́rható a .prec mezővel.

Most ı́rassunk ki egy sztringet és használjuk a .prec mezőt!

printf("%.5s","abcdefghij"); kabcde

Láthatjuk, hogy csak az első öt kartert ı́rta ki, mert sztringek esetén a a maximálisan kiı́randó karakterek
számát adja meg a .prec.

Gyakorlati tanács: a printf függvény (igazából a standard kimenet) alapvetően úgy működik, hogy
vagy összevár egy adott mennyiségű karaktert, hogy ezeket egyben kiı́rja, vagy egy adott ideig vár, ha
ez a mennyiség nem jött össze és akkor ı́rja ki az információt. Ez általában vagy 4096 karakter, vagy 30

67
másodperc.

Azért, hogy ezt az időt ne kelljen kivárni - mondjuk hibakeresés esetén - használhatjuk az fflush
függvényt. Az fflush a kérdéses fájlt, esetünkben a standard kimenetet kényszerı́ti a puffer azonnali
kiı́rására Ez azt jelenti, hogy az információ azonnal megjelenik a képernyőn. Használata:

fflush(stdout);

3.1.2. scanf

A scanf függvény a standard bemenetről olvas elemeket. A standard bemenet az esetek többségében a
billentyűzet.

A függvény deklarációja az stdio.h header fájlban található és egyszerűsı́tett formája a következő:

int scanf(const char *format,...);

A függvény egész tı́pusú. A visszatérési értéke azt adja meg, hogy hány elemet olvasott be sikeresen.

Figyelem nem azt hogy hány karaktert olvasott be. Ha egy ezer karaktert tartalmazó egyetlen sztringet
olvasunk be sikeresen, akkor 1-et ad vissza.

Hasonlóan a printf függvényhez a függvény első argumentuma a formátum sztring. Ez határozza


meg, hogy az argumentum további részében megadott változók cı́meit hogyan használja a scanf.

Nagyon lényeges a scanf függvény esetén az argumentum további részében nem a változókat, hanem
a változók cı́meit kell megadni.

A formátum sztring tartalmazhat formátum specifikátort és tetszőleges karaktereket. A formátum speci-
fikátort értelmezi, az egyéb szöveg elemeket figyelmen kı́vül hagyja.

A formátum specifikátor felépı́tése a következő:

%[*][width][length]type

A * mező azt jelenti, hogy a scanf beolvassa az adatot a standard bemenetről, de nem konvertálja és
nem adja vissza51 .

A width mező meghatározza a maximálisan beolvasható karakterek számát.

A length mező teljesen megegyezik a printf függvénynél látottakkal.


51
Tulajdonképpen ezt a beolvasandó mezőt átlépi.

68
A tı́pusok - type - gyakorlatilag megegyeznek a printf függvénynél ismertetettekkel (lásd 63. ol-
dal). Az eltérés az, hogy a lebegőpontos számok beolvasásánál az f,e,E,g,G csak azt jelenti, hogy
valamilyen formában lebegőpontos számot olvasunk be.

A scanf használata azonban nem egyszerű. Vegyük példának a következő esetet!

Adott egy karakter tömb, amelyben egy teljes (vezeték és kereszt) nevet szeretnénk tárolni. Kérjük be a
nevet!

A jelölésrendszer azonos a printf függvénynél látottakkal. Az ENTER billentyű leütését a ←֓ karakter


szimbolizálja.

scanf("%s",name);

Ekkor beı́rjuk, hogy

kTojásTóbiás←֓

Látjuk, hogy a név belsejében egy szóköz karaktert ütöttünk. Ha az eredményt kiı́ratjuk csak azt láthatjuk,
hogy

kTojás

Ez azért következett be, mert a szóköz karakter egy adott mező beolvasását lezárja.

Megjegyzés: a sor beolvasása csak az ENTER billentyű leütésére következik be, de a mezőt a szóköz és
a tabulátor karakter is lezárja52 .

Olvassunk be három egész értékű változót! A beolvasás:

scanf("%i%i%i",&i,&j,&k);

Ne feledkezzünk el a cı́mképző operátorokról a változók előtt.

Ekkor beı́rhatjuk a számainkat többféleképpen is.


52
Sajnos.

69
a) k1 2 3←֓

b) k1 2←֓
k3←֓

c) k1←֓
k2 3←֓

d) k1←֓
k2←֓
k3←֓

A szóközök tabulátor karakterrel is helyettesı́thetők.

A példából látható, hogy legalább négyféleképpen bevihetjük a számokat a beolvasás során. Ez komo-
lyan zavaró lehet.

A beolvasás formátuma a formátum specifikátortól függ. Ha a változó tı́pusa, amelynek cı́mét megadjuk
a kérdéses helyen nem felel meg a formátum specifikátorban megadott tı́pusnak, akkor esetleg a fordı́tás
során kapunk egy figyelmeztetést. A futás során viszont a scanf azt fogja csinálni, amit megadtunk
neki. Például ha azt ı́rtuk elő, hogy olvasson be egy double változót, de egy karakter tı́pusú változó
cı́mét adjuk meg, akkor arra cı́mre olvas be egy 8 bájt hosszúságú adatot. Ekkor nagy valószı́nűséggel a
program hibával kilép, vagy esetleg lefagy.

Tipikus másik veszély a sztringek beolvasásánál a sztring túlı́rása. Ez azt jelenti, hogy több karaktert
akarunk beı́rni az adott helyre, mint amekkora hely rendelkezésre áll, ezen a problémán segı́thet a width
mező alkalmazása.

3.2. Fájl kezelés

Definı́ció: fáj az a logikailag és fizikailag összefüggő állomány, amely valamely háttértárolón foglal
helyet.

A C nyelv a UNIX operációs rendszerből örökölten a fájlokat egy dimenziós bájt tömböknek - ha jobban
tetszik - karakter tömböknek tekinti.

Definı́ció: fájl pozı́ció mutatónak nevezzük azt az index értéket, amely megmutatja, hogy a fájl elejétől
számı́tva melyik pozı́ción hajtódik végre a következő művelet.

A fájl pozı́ció mutató alapműveletek (ı́rás - olvasás) automatikusan csak növekedhet az érintett bájtok
számától függően. Egyéb mozgatására speciális függvényeket kell alkalmazni.

A fájl használatához először a kérdéses fájlt meg kell nyitni. A megnyitás alapvetően történhet:

olvasásra Ekkor az állományból csak olvashatunk.

70
ı́rásra Ekkor az állományba csak ı́rhatunk.

ı́rásra - olvasására Ekkor mindkét műveletet végezhetjük.

hozzáfűzésre Ekkor tulajdonképpen az állományt ı́rásra nyitjuk meg, de a fájl pozı́ció mutató a fájl
végére mutat.

Néhány környezet különbséget tesz úgynevezett text és bináris fájlkezelés között.

A text jellegű fájl kezelés esetén a fájlban található CR LF szekvenciákból a beolvasás után csak az LF
marad meg, illetve a kiı́ráskor egy LF karakter esetén CR LF karakterek kerülnek az állományba

A bináris jellegű fájl kezelésnél a beolvasás és a kiı́rás esetén a rendszer mindent beolvas és kiı́r a tarta-
lomtól függetlenül.

A C programozási nyelv kétféle fájlkezelést valósı́t meg, ezek: az alacsony szintű és a magas szintű
fájlkezelés. A mindkét mód az alap fájlkezelő műveleteket ismeri, azonban szolgáltatásaik különböznek.

A két fájlkezelési mód átjárható. Vagyis egy alacsony szinten megnyitott fájl egy függvényhı́vás után
magas szinten is kezelhető és viszont.

3.3. Alacsony szintű fájlkezelés

Az alacsony szintű fájlkezelés esetén a fájlt egy úgynevezett fájl leı́ró azonosı́tja. Ez a fájl leı́ró egy int
tı́pusú változó53 .

Az alacsony szintű fájlkezeléshez szükséges header állományok:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

Windows-os környezetben még szükséges lehet a

#include <io.h>

Nem ismertetünk minden függvényt, csak a legfontosabbakat. Ezek viszont bőven elegendők a fájlkezeléshez.

3.3.1. open függvén

Az open, mint azt neve is mutatja egy fájl megnyitására szolgál, deklarációja a következő:
53
Ez tulajdonképpen egy index változó, amely az rendszer adott területén található struktúrákból álló tömb egy adott
elemének az indexe. A kiválasztott struktúra tartalmazza a fájl kezelésével kapcsolatos összes információt.

71
int open(const char *path,int flags,mode_t mode); vagy:

int open(const char *path,int flags);

A függvény első paramétere path a függvény elérési útja. Itt adjuk meg szövegként, hogy melyik fájlt
szeretnénk megnyitni54 .

A második paraméter a flags. Ezzel állı́tjuk be, hogy a megnyitás milyen módon történjen A legfon-
tosabb flag-ek a következők:

O_RDONLY a kérdéses állományt csak olvasásra nyitjuk meg,

O_WRONLY a kérdéses állomány csak ı́rásra nyitjuk meg,

O_RDWR a kérdéses állományt ı́rásra és olvasásra nyitjuk meg,

O_APPEND a kérdéses állományt hozzáfűzésre nyitjuk meg,

O_CREAT ha az állomány nem létezik, akkor az open függvény létrehozza,

O_DIRECT a fájl megnyitása után minimalizálja az átmeneti tár méretét és a lehető leggyorsabban küldi
ki a fájlba ı́rt adatokat,

O_EXCL ha már egy olyan fájlt szeretnénk létrehozni, amely létezik, akkor hibaüzenetet kapunk,

O_NONBLOCK ha a olvasás történik, de a fájl esetleg üres az olvasó függvény nem fog rajta várakozni,

O_TRUNC ha ı́rásra (és olvasására) nyitunk meg egy fájlt, akkor 0 -ra csökkenti a méretét,

O_BINARY a fájlt bináris módban nyitja meg,

O_TEXT a fájlt text módban nyitja meg.

Ezek a paraméterek valóban flag bitek. Abban az esetben, ha összetett paraméterezést szeretnénk meg-
adni, azt bitenkénti vagy kapcsolattal tehetjük meg. Például hozzunk létre egy állományt, nyissuk meg
ı́rásra és ha már volt előzőleg állomány, akkor vágjuk azt 0 hosszúságúra. Ekkor a flag mező:

O_CERAT | O_WRONLY | O_TRUNC

A harmadik mező a mode mező. Használata nem kötelező. Itt állı́thatjuk be a megnyitott, illetőleg
létrehozott fájl attribútumát. Ez a mező operációs rendszer függő.

Linux esetén, ezek:

S_IRWXU a fájl tulajdonosa olvasási, ı́rási és végrehajtási joggal rendelkezik,


54
Ne feledkezzünk el azonban arról, hogy windows-os környezetben a könyvtárakat \karakterrel választjuk el. A \a C-ben
azt jelenti, hogy speciális karakter következik. Ekkor ha konstansként adjuk meg a fájl nevét, akkor a \helyet \\ı́rjunk.

72
S_IRUSR a fájl tulajdonosa olvasási joggal rendelkezik,

S_IWUSR a fájl tulajdonosa ı́rási joggal rendelkezik,

S_IXUSR a fájl tulajdonosa végrehajtási joggal rendelkezik,

S_IRWXG a fájl tulajdonosának csoportja olvasási, ı́rási és végrehajtási joggal rendelkezik,

S_IRGRP a fájl tulajdonosának csoportja olvasási joggal rendelkezik,

S_IWGRP a fájl tulajdonosának csoportja ı́rási joggal rendelkezik,

S_IXGRP a fájl tulajdonosának csoportja végrehajtási joggal rendelkezik,

S_IRWXO a többiek olvasási, ı́rási és végrehajtási joggal rendelkeznek,

S_IROTH a többiek olvasási joggal rendelkeznek,

S_IWOTH a többiek ı́rási joggal rendelkeznek,

S_IXOTH a többiek végrehajtási joggal rendelkeznek.

Windows-os környezetben a két leggyakoribb mode:

S_IWRITE a fájl ı́rható,

S_IREAD a fájl olvasható.

Ezek a paraméterek is biteket jelentenek, tehát ha több jogot szeretnénk összeszerkeszteni, akkor a bi-
tenkénti vagy kapcsolatot kell használnunk - hasonlóan a flag mezőnél látottakkal.

A függvény tı́pusa int, a függvény itt adja meg a fájl azonosı́tóját, amelyet a továbbiakban a fájl azo-
nosı́tására használunk.

Ha a megnyitás esetén bármilyen hiba lép fel, akkor a visszaadott érték -1.

A fentiek alapján nyissunk meg windows-os környezetben egy text.txt állományt a gyökér könyvtárban
olvasásra, binárisan.

{
int hnd;
:
hnd=open("c:\\text.txt",O_RDONLY | O_BINARY);
:

73
3.3.2. close függvény

Ha megnyitottunk egy fájlt az le is kell tudni zárni. Erre a célra szolgál a close függvény, melynek
deklarációja a következő:

int close(int hnd);

A függvény paraméterként megkapja a fájl azonosı́tót.

Ha a lezárás sikeres, akkor a függvény 0 értékkel, ha hiba történt -1 értékkel tér vissza.

3.3.3. read függvény

A read függvény a fájlból olvas be adatokat a memóriába.

Deklarációja a következő:

ssizet_t read(int hnd,void *buff,size_t size);

Az első paraméter a int hnd. Ezzel azonosı́tjuk a fájlt, amelyből olvasni szeretnénk.

A void *buff paraméter annak a memória területnek a kezdőcı́me, ahova a beolvasás történik55 .

A size_t size az mondja meg a függvénynek, hogy hány bájtnyi adatot szeretnénk a fájlból ol-
vasni56 .

A read ssizet_t tı́pusú függvény. Sikeres beolvasás esetén a visszatérési érték a beolvasott bájtok
száma, sikertelen művelet esetén a visszatérési érték -1.

Memória
Háttértár
eleje

buff

hn
d
vége

size

14. ábra: A read működése


55
Azért void *, mert nem tudjuk pontosan, hogy milyen tı́pusú adatot fogunk beolvasni.
56
Ha text módban dolgozunk a size a fájlban tárolt adatok mennyisége.

74
Eljött annak az ideje, hogy az eddig tanult függvényekkel ı́rjuk egy egyszerű programot, amely a képernyőre
listázza a text.txt szövegfájlt57 .

1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 int main(void)
6 {
7 int hnd;
8 int r;
9 char c;
10 hnd=open("text.txt",O_RDONLY);
11 while(1)
12 {
13 r=read(hnd,&c,1);
14 if(r<1) break;
15 printf("%c",c);
16 }
17 close(hnd);
18 return 0;
19 }

A program 1. - 4. sorában a szükséges header fájlokat olvassuk be (Linux verzió).

A 7. sorban a fájl azonosı́tásához használt hnd nevű fáj azonosı́tót deklaráljuk.

A 10. sorban megnyitjuk a fájlt olvasására (nem ellenőrizzük a hibát).

A 11. sorban egy végtelen ciklust kezdünk.

A 13. sorban egyetlen bájtot olvasunk a fájlból. A read függvény visszaadott értékét az r változóba
tesszük.

Ha a 14. sorban az r változó értéke kisebb, mint 1, akkor egy break utası́tással kilépünk a ciklusból.

A 15. sorban kiı́ratjuk a beolvasott karaktert (bájtot).

Amennyiben kiléptünk a ciklusból a 17. sorban lezárjuk a fájlt.

A 18. sorban kilépünk a programból.


57
A text.txt -t előzőleg valakinek létre kell hoznia az aktuális könyvtárban.

75
3.3.4. write függvény

A write függvény a memóriából ı́r adatokat ki a megnyitott fájlba. Deklarációja a következő:

ssizet_t write(int hnd,void *buff,size_t size);

A függvény első paramétere a fájl leı́ró.

A második paraméter az a memória cı́m, ahonnan az adatokat kiı́rjuk a háttértárra.

A harmadik paraméter a kiı́randó bájtok száma a memóriában.

Memória
Háttértár

eleje
buff
size

hn
d
vége

15. ábra: A write működése

3.3.5. A tell és az lseek függvények

A tell függvény Linux alatt nem implementált függvény. Windows rendszerek alatt a függvény dek-
larációja:

long tell(int hnd);

A függvény a hnd által azonosı́tott fájl pozı́ció mutató értékét adja vissza. Hiba esetén a visszaadott
érték -1.

Az lseek feladata a fájl pozı́ció beállı́tása. Deklarációja:

off_t lseek(int hnd,off_t offset,int whence);

Az első paraméter a hnd, amellyel a fájlt azonosı́tjuk.

76
A második paraméter az offset. Ez mondja meg, hogy hogyan változtassuk meg a fájl pozı́ció mutatót.

A harmadik paraméter a whence. Ez azt mutatja meg, hogy a módosı́tást honnan értelmezzük. Ez lehet:

SEEK_SET a módosı́tást a fájl elejétől számı́tva végzi. A 0-ás fájl pozı́ció értékhez adja hozzá az offset
értékét,

SEEK_CUR a módosı́tást az aktuális pozı́ciótól számı́tva végzi,

SEEK_END a módosı́tást a fájl végétől számı́tva végzi a függvény.

A fejezet elején emlı́tettük, hogy a tell függvény a Linux rendszerek esetén nincs implementálva. Az
lseek lehetővé teszi, hogy megkapjuk a fájl pozı́ció mutató aktuális értékét.

long position;
:
position=lseek(hnd,0,SEEK_CUR);
:

Vagyis állı́tsuk a fájl pozı́ció mutatót az aktuális pozı́ciótól 0 offsettel. Ekkor a visszatérési érték a fájl
pozı́ció mutató értéke (amely nem változott).

Az lseek abban is segı́t, hogy egy fájl méretét meghatározzuk. Erre mutat példát a következő függvény
definı́ció.

1 long filelength(int hnd)


2 {
3 long pos;
4 long lgt;
5 pos=lseek(hnd,0,SEEK_CUR);
6 if(pos==-1) return -1;
7 lgt=lseek(hnd,0,SEEK_END);
8 lseek(hnd,pos,SEEK_SET);
9 return lgt;
10 }

3.3.6. fcntl függvény

Az fcntl függvény arra szolgál, hogy egy megnyitott fájl beállı́tásait utólag megváltoztathassuk. Dek-
larációja a következő:

int fcntl(int hnd,int cmd,...);

77
Az függvény első paramétere a fájl leı́ró.

A második paraméter a cmd parancs.

A parancsok a teljesség igénye nélkül:

F_DUPFD a megnyitott fájl leı́róját megismétli, ha az a harmadik paraméterben megadott értékeknél


nagyobb, vagy egyenlő. A függvény visszatérési értéke az új fájl leı́ró, vagy hiba esetén -1.

F_GETFD a kérdéses fájl flag -jeit olvassa vissza (a flag-ek határozzák meg a fájl megnyitási módját lásd
open). A harmadik paramétert figyelmen kı́vül hagyja. A kimeneti érték a visszaolvasott flag-ek.

F_SETFD a kérdéses fájl flag -jeit ı́rja át. A harmadik paraméter az ı́randó flag-ek.

A harmadik paramétert lásd az előző pontnál.

Tipikus alkalmazás az, hogy egy normál megnyitott állományt nemblokkolóra állı́tunk át (a windows
nem tudja).

int flags;
:
flags=fcntl(hnd,F_GETFL);
flags|=O_NONBLOCK;
fcntl(hnd,F_SETFL,flags);
:

3.4. Magas szintű fájlkezelés

A magas szintű fájlkezelés esetén az azonosı́tás az stdio.h fájlban definiált FILE tı́pusú struktúra
mutatóval történik.

A magas szintű fájlkezeléshez csak az stdio.h header fájlra van szükség. Ráadásul ez platformfügget-
len, tehát mindegy milyen környezetben dolgozunk.

3.4.1. fopen függvény

Magas szint esetén a fájl megnyitását végzi. Deklarációja:

FILE *fopen(const char *path,const char *mode);

A függvény első paramétere a fájl elérési útja (lásd open függvény 71. oldal).

A második paraméter az úgynevezett mód sztring. Ez a következő lehet:

78
"r" a fájlt csak olvasásra nyitjuk meg.

"w" a fájlt ı́rásra és létrehozásra nyitjuk meg. Ha már előzőleg létezett a fájl azt a megnyitás 0 hosszúságúra
vágja le.

"a" a fájlt hozzáfűzésre nyitjuk meg.

"r+" a fájlt olvasásra és ı́rásra nyitjuk meg. Ha előzőleg létezett a fájl, az a megnyitáskor nem változik.

"w+" a fájlt órásra, létrehozásra és olvasásra nyitjuk meg. Ha már előzőleg létezett a fájl azt a megnyitás
0 hosszúságúra vágja le

"a+" a fájlt hozzáfűzésre és olvasásra nyitjuk meg

Amennyiben a rendszer különbséget tesz a bináris és text jellegű fájlkezelés között, akkor a mód sztring-
hez hozzáfűzhető bináris esetben egy b, text esetben egy t karakter. Például egy olvasásra, ı́rásra meg-
nyitott bináris állomány mód sztringje: "r+b".

A függvény visszatérési értéke sikeres megnyitás esetén a kérdéses állományt leı́ró FILE struktúra cı́me.
Hiba esetén a függvény NULL pointerrel tér vissza.

3.4.2. fclose függvény

Ha megnyitottuk az állományt le is kell tudni zárni. Ezt végzi el magas szinten az fclose. Deklarációja:

int fclose(FILE *fp);

A függvény paramétere a fájlt azonosı́tó FILE mutató.

A visszatérési érték sikeres lezárás esetén 0, hiba esetén -1.

3.4.3. getc és putc függvény

A getc függvény egyetlen bájt beolvasását végzi a megnyitott fájlból. Deklarációja:

int getc(FILE *fp);

Érdekes megfigyelni, hogy ugyan egyetlen bájtot olvasunk a kérdéses fájlból, de a függvény tı́pusa int.
Ennek oka az, hogy a beolvasás teljesen transzparens kell, hogy legyen. Ez azt jelenti, hogy az egy bájton
ábrázolható 0, 1, . . . , 255 értékeket be kell tudnia olvasni. Ezt int változón ábrázoljuk, szóval ezzel
nincs gond, azonban ezt a karakter tı́pus is tudná. Karakter tı́pus használatakor viszont nem maradna
érték a hibajelzésre.

79
A hibajelzést úgy oldja meg a függvény, hogy hiba esetén -1 értéket ad vissza. Ez az oka, hogy a
függvény tı́pusa int.

A putc függvény egy bájt kiı́rását végzi a megnyitott fájlba. Deklarációja:

int putc(int chr,FILE *fp);

érdekes, hogy a függvény első paramétere, amely a kiı́randó bájt mégis int tı́pusú. Ennek történelmi
okai vannak. Az ős C csak az int tı́pust ismerte. Ez a függvény tehát az őskorból származik.

A függvény sikeres kiı́rás esetén a kiı́rt bájt értékével tér vissza. Hiba esetén a visszatérési érték -1.

Nézzünk egy példát, amely egy fájlt másol át bájtról bájtra, hibakezeléssel!

1 #include <stdio.h> 1
2 int main(void) 2
3 {
4 FILE *fp;
5 FILE *cp;
6 int chr;
7 fp=fopen("source.txt","r");
8 if(fp==NULL) return 1;
9 cp=fopen("destination.txt","w");
10 if(cp==NULL) return 1;
11 while(1)
12 {
13 chr=getc(fp);
14 if(chr==-1) break;
15 putc(chr,cp);
16 }
17 fclose(fp);
18 fclose(cp);
19 return 0;
20 }

A 4. sorban deklaráljuk annak a fájlnak az azonosı́tóját, amely a másolás forrása lesz.

Az 5. sorban a c él fájl azonosı́tója kerül deklarálásra.

A 6. sorban az az egész tı́pusú változó kerül deklarálásra, amely majd a beolvasott, illetve kiı́rt bájt
értékét tartalmazza.

A 7. sorban megnyitjuk a forrásállományt csak olvasásra.

80
A 8. sorban megvizsgáljuk, hogy a megnyitás sikertelen volt-e. Ha sikertelen 1-es értékkel kilépünk a
programból.

A 9. és 10. sorban szintén ezt tesszük azzal a különbséggel, hogy a célállományt ı́rásra és létrehozásra
nyitjuk meg.

A 11. sorban egy végtelen ciklust indı́tunk.

A 13. sorban beolvassuk a következő karaktert.

A 14. sorban megvizsgáljuk, hogy a beolvasott érték hibát jelez-e. Ha igen, akkor elhagyjuk a ciklust.

A 15, sorba akkor jut a program, ha a getc sikeres beolvasást hajtott végre. Ekkor a kérdéses értéket
kiı́rjuk a cél állományba.

A 16. és 17. sorban lezárjuk a fájlokat.

A 18. sorban elhagyjuk a programot 0 értékkel.

3.4.4. fprintf függvény

Az fprintf formátumozott kiı́rást tesz lehetővé egy fájlba éppúgy, mint a 63 oldalon leı́rt printf
függvény teszi a standard kimenetre. Deklarációja:

int fprintf(FILE *fp,const char *format, ... );

A függvény első paramétere a fájl azonosı́tója

A többi paraméter és a visszatérési érték minden pontban megegyezik a 63. oldalon leı́rt printf
függvény paramétereivel.

Amennyiben hibaüzenetet szeretnénk kiı́rni, célszerű az fprintf -et használni úgy, hogy az stderr
-re ı́runk, például:

fprintf(stderr,"Ez egy hibauzenet\n");

Ne feledjük, hogy az stderr állományt nem kell megnyitnunk, mert alapértelmezetten nyitott.

3.4.5. scanf függvény

A fscanf függvény a ??. oldalon ismertetett scanf függvényhez hasonlóan formátumozott beolvasást
tesz lehetővé a kiválasztott fájlból. Deklarációja a következő:

81
int fscanf(FILE *fp,const char *format, ... );

Az fscanf esetén figyelnünk kell, hogy a fájl pozı́ció a megfelelő pozı́cióban álljon, különben a
függvény rossz értéket olvas be. Ekkor érdemes a függvény visszatérési értékét figyelni, mert ha nem
olyan tı́pust talál a fájlban, mint a várt, akkor a beolvasás hibás.

A függvény első paramétere a fájl azonosı́tója. A többi paraméter és a visszatérési érték megegyezik a
?? oldalon ismertetett scanf függvénnyel.

3.4.6. fflush függvény

A korszerű operációs rendszerek próbálnak a számı́tógép erőforrásával takarékoskodni. Ez fájlba ı́ráskor


az jelenti, hogy nem bájtonként ı́rják ki az információt, hanem egy adott adatmennyiségig várnak és csak
akkor fordulnak a kérdéses perifériához. Ez a méret rendszerfüggő, de a legelterjedtebb méret 4096 bájt.

Ha adott idő belül nem jön össze a fent emlı́tett méret, akkor a rendszer mindenképpen kiı́rja az adatokat.
Ez az idő szintén rendszerfüggő. Egy sűrűn használt időkorlát a 30 másodperc.

Megjegyzés: természetesen a fájl lezárásakor az adatok kiı́ródnak.

Ez a tulajdonság néhány esetben nagyon kellemetlen. Például akkor, ha a programunk futását a képernyőre
ı́rással próbáljuk követni. Ekkor célszerű a fflush használata.

Az fflush a kérdéses fájlba ı́rást a meghı́vásakor azonnal megkezdi. Deklarációja:

int fflush(FILE *fp);

Sikeres végrehajtás esetén a visszatérési érték 0, egyébként -1.

Ha az fflush argumentumába NULL pointer ı́runk, vagy üresen hagyjuk, akkor az összes ı́rásra nyitott
fájlra végrehajtja a kiı́rást.

Ha tehát a képernyőre ki akarjuk ı́ratni azonnal az információt, akkor a következőt célszerű tenni:

printf("Szoveg\n");fflush(stdout);

3.4.7. fread és fwrite függvények

Az fread függvény - hasonlóan a read függvényhez - a megnyitott fájlból olvas be a memóriába.


Deklarációja a következő:

size_t fread(const void *ptr,size_t size,size_t n,FILE *ptr);

82
A függvény első paramétere a kérdéses memória cı́m, ahova olvasni szeretnénk.

A második paraméter megadja, hogy a mekkora egy beolvasandó elem mérete.

A harmadik paraméter megmondja, hogy a kérdéses elemekből hány darabot akarunk beolvasni.

A negyedik paraméter a fájl azonosı́tója.

A függvény visszatérési értéke sikeres beolvasás esetén a beolvasott elemek száma. Hiba esetén 0
(néhány rendszer esetén -1, ekkor a függvény tı́pusa int ).

size_t fwrite(const void *ptr,size_t size,size_t n,FILE *ptr);

A függvény első paramétere a kérdéses memória cı́m, ahonnan ki akarjuk az adatokat ı́rni.

A második paraméter megadja, hogy a mekkora egy kiı́randó elem mérete.

A harmadik paraméter megmondja, hogy a kérdéses elemekből hány darabot akarunk kiı́rni.

A negyedik paraméter a fájl azonosı́tója.

A függvény visszatérési értéke a sikeresen kiı́rt elemek száma. Hiba esetén 0 (néhány rendszer esetén -1,
ekkor a függvény tı́pusa int ).

3.4.8. ftell és fseek függvények

Az ftell függvény megmondja, hogy a fájl pozı́ció mutató hol áll a kérdéses fájlban. Deklarációja:

long ftell(FILE *fp);

Sikeres függvényhı́vás esetén a visszatérési érték a fájl pozı́ció mutató értéke. Hiba esetén -1.

Az fseek függvény a 76. oldalon ismertetett lseek -hez hasonlóan a fájl pozı́ció mutató értékét állı́tja
be.

long fseek(FILE *fp,long offset,int whence);

Az első paraméter a fájl azonosı́tója. A többi paraméter és a visszatérési érték teljesen megegyezik a
lseek függvénynél leı́rtakkal (lásd 76. oldal).

Vegyük példának azt feladatot, hogy a text.txt fájlban az e karaktereket E karakterekre cseréljük58 .
58
Ez egy iskolapélda, sokkal optimálisabban is megı́rható.

83
1 #include <stdio.h>
2 int main(void)
3 {
4 FILE *fp;
5 int chr;
6 long pos;
7 fp=fopen("text.txt","r+");
8 if(fp==NULL)
9 {
10 fprintf(stderr,"Nyitasi hiba\n"); return 1;
11 }
12 while(1)
13 {
14 pos=ftell(fp);
15 chr=getc(fp);
16 if(chr==-1) break;
17 if(chr==e)
18 {
19 fseek(fp,pos,SEEK_SET);
20 putc(fp,E);
21 }
22 }
23 fclose(fp);
24 return 0;
25 }

A program eleje már az előző mintaprogramokból ismert.

A program 6. sorában egy long tı́pusú változót deklarálunk, amelybe a fájl pozı́ció mutatót fogjuk
elhelyezni menden beolvasás előtt.

A 7. sorban a text.txt allományt nyitjuk meg ı́rásra és olvasásra "r+".

Ha sikertelen a megnyitás, akkor 8. sorban az if argumentuma igaz.

A 10. sorban a standard hiba fájlra kiı́rjuk a hibát és elhagyjuk a programot egy 1-es értékkel.

A 12. sorban egy végtelen ciklust kezdünk.

A 14. sorban az éppen aktuális beolvasandó karakter pozı́cióját eltesszük a pos változóba.

A 15. sorban beolvassuk a karaktert.

A 16. sorban megvizsgáljuk, hogy a beolvasás sikeres volt-e. Ha nem elhagyjuk a ciklust.

84
A 17. sorban megvizsgáljuk, hogy a beolvasott karakter e -e.

Ha igen a program a 19. sorban folytatódik, ahol a beolvasás előtti pozı́cióra állı́tjuk a fájl pozı́ció
mutatót.

A 20. sorban kiórjuk az E karaktert.

A 22. sorban zárjuk le a végtelen ciklust.

Innetől a program működése már ismert.

3.5. Átjárás a két fájlkezeéls között

Előzőleg már emlı́tettük, hogy az alacson és a magas szintű fájl kezelés között van átjárás mindkét
irányban. Azt is láthattuk, hogy a mindkét módszernek megvannak az előnyei és a hátrányai. Ezért
érdemes a ismernünk a következő függvényeket azért, hogy a lehető legjobban ki tudjuk a két módszer
tulajdonságait.

3.5.1. fileno függvény

A fileno függvény egy magas szinten megnyitott fájl fájl leı́róját adja vissza. Deklarációja, amely az
stdio.h -ban található, a következő:

int fileno(FILE *fp);

Ha az fp -vel azonosı́tott fájl létezik a visszatérési értéke a fájl leı́rója, ha nemlétezik, akkor -1.

A következő példában az stdin fájlt állı́tjuk át nem blokkolóvá.

1 #include <stdio.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 int main(void)
5 {
6 int hnd;
7 int r;
8 int f;
9 char buff[80];
10 hnd=fileno(stdin);
11 f=fcntl(hnd,F_GETFL);
12 f|=O_NONBLOCK;
13 fcntl(hnd,F_SETFL,f);

85
14 while(1)
15 {
16 r=scanf("%s",buff);
17 if(r==1)
18 {
19 printf("%s",buff);
20 break;
21 }
22 printf(".");
23 fflush(stdout);
24 }
25 return 0;
26 }

A program eleje a 9. sorig ismert.

A 10. sorban az alapértelmezetten nyitott standard bemenet fájl leı́róját ( hnd ) kapjuk meg a fileno
függvénnyel.

A 11. sorban az fcntl segı́tségével lekérdezzük a fájl flag -jeit az f változóba.

A 12. sorban az O_NONBLOCK flag-et rámásoljuk a flag -ekre

A 13. sorban az átalakı́tott flag -eket beállı́tjuk

A 14. sorban egy végtelen ciklust indı́tunk.

A 15. sorban hı́vjuk a scanf függvényt. A függvény visszaadott értékét az r változóban tároljuk.

Ha az r változó értéke 1, akkor a scanf olvasott be értékelhető inputot és az igaz ág végrehajtódik (lásd
sor).

Ha történt beolvasás, akkor azt kiı́rjuk a képernyőre - 19. sor és elhagyjuk a végtelen ciklust - 20. sor.

Abban az esetben, ha nem történt értékelhető beolvasás, a program a 22. sorban folytatódik, ahol egy .
karaktert ı́r ki a képernyőre.

A 23. sorban a kiı́ratást kényszerı́tjük az fflush függvénnyel.

A program további részei ismertek.

Megjegyzés: ez a program windows alatt nem megy. Linux-on és BSD UNIX-okon problémamentes a
futtatása.

86
3.5.2. fdopen függvény

Az fdopen függvény lehetővé teszi, hogy egy alacsony szinten megnyitott fájl konvertálható legyen
magas szintre. A függvény deklarációja:

FILE *fdopen(int hnd,const char *mode);

A függvény első paramétere a fájl leı́ró.

A második paraméter a az úgynevezett mód sztring. Ez teljesen megegyezik az fopen függvényben


ismertetett mód sztringgel (lásd 78. oldal).

A függvény visszatérési értéke a kı́vánt FILE pointer. Ha a konverzió sikertelen a visszatérési érték
NULL pointe

3.6. Amire figyelni kell (intelmek a fájlkezeléssel kapcsoltaban)

A fájl kezelés C alatt nem bonyolult dolog, azonban néhány dologra célszerű figyelni. A szempontok a
következő:

• a megnyitott fájlt minden esetben zárjuk le,

• kerüljük a ciklusokban fájlok megnyitását. Ha mégis szükséges, akkor annyiszor zárjuk le, ahányszor
megnyitottuk. Erre különösen figyeljun a break és continue utası́tások esetén.

• ha egy függvénybe belépünk és ott nyitunk fájlt, akkor a kilépés előtt zárjuk is le minden kilépési
pont előtt.

87
4. Dinamikus memória kezelés

A gyakorlati életben sokszor előforduló probléma, hogy nagy mennyiségű memóriára van szükség egy
adott feladat elvégzéséhez. Ebben az esetben nem célszerű ezt a memória mennyiséget változóterületként
deklarálni, mert vagy valamilyen a rendszer által felállı́tott korlátozás nem engedi meg, vagy egyszerűen
fölöslegesen nagy programot készı́tünk.

Ilyen esetekre a dinamikus memória kezelés a helyes megoldás59

4.1. A memória lefoglalása malloc , calloc , realloc

Első lépés a dinamikus memória kezelésnél a memória lefoglalása. Sikeres lefoglalás esetén kapunk egy
összefüggő memória tartományt, amelyet a kezdőcı́mével azonosı́t a program60

Erre a célra a legegyszerűbb a malloc függvény használata. Deklarációja az stdlib.h header


fájlban található meg.

void *malloc(size_t size);

A függvény egyetlen paramétere azt mondja meg, hogy hány bájtot szeretnénk a memóriában lefoglalni.

A visszatérési érték a lefoglalt memória kezdőcı́me. Ha a lefoglalás sikertelen, akkor NULL pointer.

Ne feledkezzünk meg arról, hogy a visszaadott pointer void tı́pusú. Ennek oka, hogy nem tudjuk, hogy
milyen tı́pusú a lefoglalt memória.

A memória lefoglalásra használható a calloc függvény is. Ez annyiban különbözik, hogy a lefoglalt
memória méretét nem bájtban, hanem a kı́vánt tı́pus méretében kell megadni. Deklarációja:

void *calloc(size_t n,size_t size);

Az első paraméter a lefoglalandó elemek száma, a második paraméter a a kérdéses tı́pus mérete.

A visszatérési érték teljesen megegyezik a malloc esetén leı́rtakkal.

Ilyenkor felmerül a kérdés, hogy miért nem ad vissza olyan pointert, mint, aminek a méretét megadjuk
a függvény argumentumában. A válasz egyszerű, a függvény nem tudhatja, hogy milyen tı́pust adtunk
meg, mert csak a méretet adtuk meg. Arra nincs lehetőség, hogy a tı́pust adjuk meg.

Ha szükség van a kérdéses tı́pushoz rendelt pointerre, akkor erről a programozónak kell gondoskodnia a
programban Például:
59
Azt azért jegyezzük meg, hogy vannak olyan területek, ahol a dinamikus memória kezelés nem megengedett. Ilyen például
a biztonságkritikus szoftverek területe.
60
Igazából az operációs rendszer.

88
long *p;
:
p=(long *)calloc(1000,sizeof(long));
:

A visszaadott tı́pus void *, de nekünk long *-ra van szükségünk, ezért a függvény kimeneti értékét
”castoljuk” kı́vánt tı́pusra61 .

Gyakran előforduló probléma, hogy a lefoglalt memória mérete nem megfelelő. Ekkor használhatjuk a
realloc függvényt.

void *realloc(void *ptr,size_t size);

Az átméretezendő memóriát a ptr pointer azonosı́tja. Az igényelt új memória méretet a size változó
adja meg. Ha sikeres a lefoglalás, akkor az újonnan lefoglalt memória kezdőcı́mét kapjuk meg, ha
sikertelen a függvény NULL poiterrel tér vissza.

Az átméretezett memória eredeti tartalmát semmi nem garantálja. Elképzelhető, hogy az újonnan lefog-
lalt terület teljesen máshol helyezkedik el, mint a régi.

4.2. A lefoglalt memória felszabadı́tása free

Amennyiben dinamikusa memóriát foglaltunk le, azt fel is kell szabadı́tani. Ezt a műveletet a free
függvény végzi.

void free(void *ptr);

A free bemeneti paramétere a memóriát azonosı́tó pointer a ptr.

4.3. Amire figyelni kell (intelmek dinamikus memóriakezelés esetén)

A dinamikus memória kezeléssel kapcsolatban a következő szabályokra kell figyelni:

• a lefoglalt memóriát minden esetben szabadı́tsuk fel,

• kerüljük a dinamikus memória foglalást ciklusokban. Ha ez nem lehetséges, akkor gondoskodjunk


arról, hogy minden kilépési pontban legyen megfelelő felszabadı́tás.

• ha egy függvény belsejében foglalunk le dinamikusan memóriát, akkor a kilépéskor szabadı́tsuk


fel62 .
61
Ez igazából inkább C++ probléma. A C erre nem érzékeny.
62
Hacsak nem speciális céllal tesszük, de ha lehet ezt az esetet kerüljük.

89
PROGRAMOZÁS II.

Valkai Zoltán KVK / MAI TC 401


valkai.zoltan@kvk.uni-obuda.hu
36-1-666-5163

Valkai Zoltán ÓE/KVK/MAI 1


KÖVETELMÉNYEK

https://mai.kvk.uni-obuda.hu/
Tantárgyak / Őszi félév / Programozás II.

Valkai Zoltán ÓE/KVK/MAI 2


PROGRAMOZÁS
program: elemi utasítások véges számú lépésből álló sorozata

• első programozható elektronikus számítógép


• Electronic Numerical Integrator and Computer (ENIAC)
• USA Pennsylvania University
• 30,5 m; 30 tonna; 174 kW; 17e elektroncső; +/- 1/5000 s;
• decimális számrendszer; tízjegyű előjeles decimális számok;
„dugdosós” programozás
• Manhattan Project (3D másodfokú differenciálegyenlet)
• Teller Ede => hidrogénbomba számítások

Valkai Zoltán ÓE/KVK/MAI 3


PROGRAMOZÁSI NYELVEK
• alacsony szintű: assembly; mnemonik; HW és CPU
függő
• magas szintű: 1954 FORmula TRANslation; kőkemény
szintaktika
• cél: numerikus algoritmusok, matematikai formulák
kezelése
• 1955 ALGOritmic Language; matematikai-műszaki
számítások, dinamikus tárkezelés, rekurzió
• ►CPL; Simula; B; C

Valkai Zoltán ÓE/KVK/MAI 4


C
• 1972 Dennis Richie - Ken Thompson AT&T Bell Labs
• operációs rendszer használatlan PDP-11/20-ra; UNIX
(Multiuser, Multitasking)
• egyes C elemek a PDP sajátosságai miatt kerültek be a nyelvbe
• alap: B nyelv Ken Thompson; 1970 UNIX, de nem volt
hatékony
• 1973 UNIX kernel (rendszermag) assembly=>C-ben újraírták
• egyik első, nem assembly nyelvű kernel
• a C egyszerre alacsony és magas szintű programozási nyelv

Valkai Zoltán ÓE/KVK/MAI 5


C nyelvi elemei
• változók: használat előtt deklarálni kell
(miért?), értékadás(ok)
• operátorok: elválasztó / aritmetikai / logikai /
bit / relációs / speciális
• utasítások: elágazó / ciklus
• függvények: saját / gyári (header)
hatékony programozási nyelv alacsony és magas
szintű feladatokhoz
Valkai Zoltán ÓE/KVK/MAI 6
FÜGGVÉNYEK
• Mat.: függvény ( leképezés)
• Legyen X és Y két adott (nem szükségképpen
különböző) halmaz. Azt mondjuk, hogy f az X
halmazon értelmezett és Y beli értékeket felvevő
függvény. Az f az X halmaznak az Y halmazba való
leképezése, ha adott egy olyan utasítás (szabály,
képlet, táblázat ábra, stb.) amely az X halmaz minden
egyes x elemének megfelelteti az Y halmaz egy f(x)-
szel jelölt elemét. Az X a függvény értelmezési
tartománya, Y a függvény értékkészlete.
• y=f(x) y = f ( x1 , x2 )
Valkai Zoltán ÓE/KVK/MAI 7
MI a FÜGGVÉNY a C-ben?
// egy függvény mindig van, kötelező
int main() // a szabvány így rendelkezik
{ // logikai blokk nyitó zárójel
return 0; // normál lefutás esetén a
// szabvány 0 visszatérési
// értéket ír elő
} // logikai blokk csukó
// zárójel
Itt bemenő paraméter nincs, helyesen int main(void)
Speciális esetben a main() függvénynek lehet argumentuma

Valkai Zoltán ÓE/KVK/MAI 8


FÜGGVÉNYEK
• „GYÁRI” ezekről majd később…
• általában a fejlesztői környezettel kapjuk
• „.h” kiterjesztésűek
• funkció szerint csoportosítva
• pl.: math.h matematikai fv-ek
• complex.h komplex adatokkal
végzett műveletek
• stdio.h alapvető be- és kimeneti
függvények
• „SAJÁT”
• mi írjuk, saját céljainknak megfelelően

Valkai Zoltán ÓE/KVK/MAI 9


HOGYAN készítjük el?
MIÉRT jó ez?
• C-ben a használt változókat DEKLARÁLNI KELL!
meg kell adni a változó típusát
• C-ben a használt függvényeket DEKLARÁLNI
KELL! meg kell adni a függvény típusát
hogyan?
• a függvény típusát a
VISSZATÉRÉSI ÉRTÉK TÍPUSA határozza meg!

Valkai Zoltán ÓE/KVK/MAI 10


HOGYAN deklaráljuk a sajátfüggvényt?
• int main() // ELŐTT, mert utána
// akarjuk használni a main()-ben

• HOGYAN?

• változó deklarálása
• VÁLTOZÓ NEVE és TÍPUSA
• Értékadás kötelező?
Valkai Zoltán ÓE/KVK/MAI 11
sajátfüggvény DEKLARÁLÁSA
esetén meg kell adni a
FÜGGVÉNY NEVÉT és a FÜGGVÉNY
TÍPUSÁT

• a függvény típusát a VISSZATÉRÉSI ÉRTÉK


TÍPUSA határozza meg
• egy típus egyszerre csak egyféle lehet => egy
érték adható vissza

Valkai Zoltán ÓE/KVK/MAI 12


hogyan is néz ki az int main(void)
függvény?
• az argumentum „void” a bemenő paraméter…
• egy függvénynek tetszőleges számú bemenő
paramétere is lehet
• a bemenő paraméter(ek) olyan(ok), mint a
változó(k) => meg kell adni a bemenő
paraméter(ek) típusát/típusait

Valkai Zoltán ÓE/KVK/MAI 13


HURRÁ!
így kell deklarálni:
int fv(int a, char b);
int main(void)
{
return 0;
}
int az „fv(int a, char b)” függvény visszatérési értékének típusa
„fv” a függvény neve
két bemenő paramétere van
az első int típusú, a második char típusú
a deklarációt ; elválasztó operátor zárja!
a változókkal ellentétben minden egyes függvényt külön-külön deklarálni kell!

Valkai Zoltán ÓE/KVK/MAI 14


eddig akár jó is lehet a programunk…
• de
• mit fog tenni (számolni) a függvény a bemenő
paraméterekkel?
• ezt hol, hogyan határozzuk meg?

Valkai Zoltán ÓE/KVK/MAI 15


programunk eddig így néz ki
int fv(int a, char b); // ez itt az fv() függvény
// deklarációja
int main(void)
{
return 0;
}

Valkai Zoltán ÓE/KVK/MAI 16


HOL és HOGYAN adjuk meg a
függvény működését?
int fv(int a, char b); //ez itt az fv() függvény deklarációja
int main(void)
{
return 0;
}
int fv(int a, char b) // ez az fv(…) függvény definíciója
{
return 0; // a függvény nem „csinál” semmit, 0-t ad
// vissza
} // ez így már szintaktikailag helyes

Valkai Zoltán ÓE/KVK/MAI 17


Hogyan írjuk meg a definíciót?
Ezt a feladat határozza meg!
y = a – ab - ab2 alapján a függvény definíciója
int fv(int a, char b)
{
int w=0; //kezdőérték mindig kell?
w = a – a * b – a * b * b;
// w = a – (a * b) – (a * b * b);
return w;
}
Biztosan jó, hogy a visszaadott érték int típusú?
Valkai Zoltán ÓE/KVK/MAI 18
függvény deklaráció és definíció is van már

• ez így működik?

• a függvényt nem hívtuk meg 

• de akkor működni se fog -   

Valkai Zoltán ÓE/KVK/MAI 19


FÜGGVÉNYHÍVÁS
int fv(int a , char b);
int main(void)
{ int xa= 13, y = 0; char xb = 2;
y=fv(xa , xb); //xa ← 13, xb ← 2, xa,xb formális, 13, 2 az aktuális
//paraméterek
return 0;
}
int fv(int a , char b) /a ← 13, b ← 2 sorrend
{ int w=0; //kezdőérték mindig kell?
w = a – a * b – a * b * b;
return w;
}
Valkai Zoltán ÓE/KVK/MAI 20
Miért jó, ha függvényeket használunk,
függvényekre bontjuk a feladatot?

• főprogram átláthatóbbá, olvashatóbbá válik


• egy függvény akár többször is meghívható
különböző paraméterekkel
• a függvény csak 1x kerül fordításra, 1x kerül az
operatív mem-ba
• függvény külön készíthető, tesztelhető
• újrafelhasználható más programban
Valkai Zoltán ÓE/KVK/MAI 21
Hány bemenő paramétere lehet
egy függvénynek?

• átláthatóság

• követhetőség

• logikus darabszám

Valkai Zoltán ÓE/KVK/MAI 22


Lehet-e függvényből függvényt hívni?
• Természetesen
• Amennyiben a típusok nem egyeznek
automatikus típuskonverzió következik be
• Probléma lehet, hogy a függvény csak egy
értéket ad vissza
• Több szintű is lehet
• w=fv3( fv2( fv1(a) ) ); //sorrend

Valkai Zoltán ÓE/KVK/MAI 23


Függvény hívásban hívhatja-e a
függvény saját magát?
int fkt(int a) //csak a függvény definíció!
{
int y=1, c=0; // itt két kilépés is van, nem szeretjük
if(a<=1)
{
c++;
return 1; // követhetetlen
}
c++;
y=a+fkt(a-1); // ehhez azért rutin kell!
return y; // átláthatatlan
} // nem lehet debug-olni
Valkai Zoltán ÓE/KVK/MAI 24
Hogyan tudjuk megkülönböztetni a
változót és függvényt?
• deklaráció:
• változó: int v;

• függvény: int v( int a);

• Az azonos nevű objektumokat a fordító


általában hibának jelzi

Valkai Zoltán ÓE/KVK/MAI 25


BEMENŐ PARAMÉTER NÉLKÜLI FÜGGVÉNY

deklaráció:
int fv1(void);
visszatérési érték típusa int
bemenő paraméter nincs
Mikor használjuk?
a függvény nem a bemenő adatokon végez
műveletet
valamilyen önálló funkció megvalósítása
a visszatérési érték a funkció sikerességét jelzi
Valkai Zoltán ÓE/KVK/MAI 26
BE- és KIMENŐ PARAMÉTER NÉLKÜLI FÜGGVÉNY

deklaráció
void fv2(void);
• nincs visszatérési érték
• nincs bemenő paraméter
Mikor használjuk?
• a függvény nem a bemenő adatokon végez
műveletet
• valamilyen önálló funkció megvalósítása
• mivel nincs visszatérési érték, a működésről nem
tudunk semmit Valkai Zoltán ÓE/KVK/MAI 27
MI KÖTELEZŐ?
• sajátfüggvény hívása esetén:

• deklaráció
• definíció törzse
• akár üres is lehet!
• majd egyszer megírjuk és
teszteljük
• függvény hívás
Valkai Zoltán ÓE/KVK/MAI 28
GLOBÁLIS VÁLTOZÓ
• A függvény egy „fekete doboz”
• Több bemenete, de csak egy kimenete van
• A fekete doboz a bemenő paraméterekkel
végez műveleteket
• Ha globális változót használunk, a függvény
zártságát sértjük meg

Valkai Zoltán ÓE/KVK/MAI 29


FELADAT
Írjon egy sajátfüggvényt, amely a bemenő egész
számról eldönti, hogy az osztható-e 2-vel vagy 3-
mal!
A visszaadott érték
• legyen 0, ha nem osztható,
• legyen 1, ha osztható!

Valkai Zoltán ÓE/KVK/MAI 30


KÖSZÖNÖM A FIGYELMET!

Valkai Zoltán ÓE/KVK/MAI 31


PROGRAMOZÁS II.
2. Előadás

TÖMBÖK

© Valkai Zoltán 2020 ÓE/KVK/MAI 1


SZÁMSOROZAT FOGALMA
Def: Az olyan hozzárendelés, amely minden
természetes számhoz egy-egy számot rendel,
számsorozatot határoz meg.
Pl.:
1; 2; 3; 4; 5; …
1; 3; 5; 7; 9; …
1/1; -1/4; 1/9; -1/16
1; √2; √3; 2; √5; √6; …
© Valkai Zoltán 2020 ÓE/KVK/MAI 2
SZÁMSOROZAT
A sorozatban szereplő egyes számok a sorozat
tagjai vagy elemei.
Egy végtelen sorozatot akkor határoztunk meg,
ha megadtuk azokat a képzési szabályokat,
amelyek a sorozat bármelyik tagját
meghatározzák.
A sorozatot olyan függvénynek tekintjük,
amelynek értelmezési tartománya a természetes
számok halmaza.
© Valkai Zoltán 2020 ÓE/KVK/MAI 3
A sorozat jelölése
A sorozat tagjait indexszel ellátott azonos
betűkkel jelöljük.
Pl.: a1, a2, a3, a4, …; an; …
b1, b2, b3, b4, …; bn; …
x1, x2, x3, x4, …; xn; …
Azt, hogy az an egy számsorozat n-edik tagja, azt
{an}-nel jelöljük. Az {an} kifejezésben az n index
azt mutatja, hogy an a sorozat n-edik tagja.
© Valkai Zoltán 2020 ÓE/KVK/MAI 4
SOROZAT jelölése

Adott az alábbi számsorozat: 2; 4; 6; 8; …


A számsorozatot így jelöljük:
an = 2n, ahol n= 1, 2, 3, …
vagy röviden:
{an}=2n, vagy {2n}

© Valkai Zoltán 2020 ÓE/KVK/MAI 5


Számsorozat megadása

© Valkai Zoltán 2020 ÓE/KVK/MAI 6


MATEMATIKAI SOROZAT

© Valkai Zoltán 2020 ÓE/KVK/MAI 7


TÖMB
• absztrakció, matematikailag mátrix
• A mátrix a matematikában mennyiségek
téglalap alakú elrendezése, táblázata,
(számok,függvények, kifejezések, mátrixok;
általánosan valamilyen gyűrű vagy vektortér
elemeié).
• sokféle alkalmazás
• lineáris egyenletek
• lineáris egyenletrendszerek
© Valkai Zoltán 2020 ÓE/KVK/MAI 8
MÁTRIX
• A mátrix vízszintes vonalban elhelyezkedő
elemei sorokat, a függőleges vonalban
elhelyezkedő elemei oszlopokat alkotnak.
• Egy m sorból és n oszlopból álló mátrixot m-
szer n-es mátrixnak neveznek, ahol m és n
pozitív egészek a mátrix dimenziói.
• A mátrix dimenzióit mindig először a sorok
számával, majd azt követően az oszlopok
számával adjuk meg.
© Valkai Zoltán 2020 ÓE/KVK/MAI 9
A mátrix jelölése
┌ ┐
│a11 a12 a13 ... a1n │
│a21 a22 a23 ... a2n │
│a31 a32 a33 ... a3n │
A = │ . . . . │
│ . . . . │
│ . . . . │
│ am1 am2 am3... amn │
└ ┘
• Ai,j vagy A[i,j] először a sor száma, utána az
oszlop száma
© Valkai Zoltán 2020 ÓE/KVK/MAI 10
JELÖLÉS
• A: = (a i , j)m*n

• A[i , j] a i , j, ahol 1 ≤ i ≤ m és 1 ≤ j ≤ n

• mátrix A, mártix elemei ai,j

• programok esetén kezdő index: 0 vagy 1

© Valkai Zoltán 2020 ÓE/KVK/MAI 11


Speciális MÁTRIXOK
• ha valamelyik dimenzió 1, akkor → vektor
• sorvektor → csak 1 sora van [a1 a2 a3 an ]
• oszlopvektor → csak 1 oszlopa van
┌ ┐
│ b1 │
│ b2 │
B = │ b3 │
│ ∙ │
│ bm │
└ ┘
• 1x1 es mátrix → skalár
© Valkai Zoltán 2020 ÓE/KVK/MAI 12
MŰVELETEK
• transzponálás m x n → n x m

• összeadás (csak azonos dimenziójúak)

• skalárral való szorzás

• szorzás méret egymás transzponáltja

© Valkai Zoltán 2020 ÓE/KVK/MAI 13


KÖSZÖNÖM A FIGYELMET!

© Valkai Zoltán 2020 ÓE/KVK/MAI 14


PROGRAMOZÁS II.
3. Előadás

TÖMB a C
programozásban

© Valkai Zoltán 2020 ÓE/KVK/MAI 1


TULAJDONSÁGOK
• a tömböt a változóhoz hasonlóan deklarálni kell
• összetett adattípus (elemi adatok sorozata)
• a tömb elemei ugyanolyan (azonos) típusúak
• ugyanazzal a névvel tudunk hivatkozni a tömb
elemeire
• elemek megkülönböztetése indexszel történik
• C-ben az index MINDIG 0-tól kezdődik!!!
• a tömb csak véges méretű lehet, mert az adatok
az operatív memóriában tárolódnak

© Valkai Zoltán 2020 ÓE/KVK/MAI 2


TÖMB DEKLARÁCIÓ
• típus azonosito_nev [ elemek_darabszama ] ;
• típus bármilyen alaptípus: char / int / short int / float /
double / long double
• a char típusú tömbről később még lesz szó
• azonosito_nev betűkből és számjegyekből; mint a
változóknál
• elemek darabszáma: numerikus konstans, amelynek már
értéke kell hogy legyen a deklarációs sorban; természetes
szám ≥ 1
• ;
• int itmb [ 10 ] ; double dtmb [ 50 ] ;
• értékadás nem kötelező, de lehetséges (majd később)

© Valkai Zoltán 2020 ÓE/KVK/MAI 3


mekkora az adatTÖMB MÉRETE?
• int itmb [ 10 ] ; double dtmb [ 50 ] ;
• int y1 = 0, y2 = 0;

• y1 = sizeof ( itmb );
• y2 = sizeof ( dtmb );

• TÖMB ELEMSZÁMA * EGY ADATELEM MÉRETE

© Valkai Zoltán 2020 ÓE/KVK/MAI 4


HIVATKOZÁS tömbelemre
• kezdő index MINDIG 0, index egész típusú
deklaráció: int itmb [10] ;
itmb[ 0 ] ;
itmb [9] ;

itmb [10] ; ez jó?


itmb [-1] ; ez jó?
Mi történik?
© Valkai Zoltán 2020 ÓE/KVK/MAI 5
ÉRTÉKADÁS
• deklaráció: int itmb [10] ;
• itmb [0] = 10 ;
• itmb [2] = 20 ; // mennyi lesz a többi tömbelem értéke?

• int itmb [10] = { 10 , , 20 } ; // mennyi lesz a
// többi tömbelem értéke?
• int itmb [10] = { 0 } ; // mennyi lesz a többi
// tömbelem értéke?
• int itmb[10] = { 0 } , i=0 ; // MINDEN tömbelem 0
// értéket vesz fel
• for ( i= 0 ; i<10 ; i++)
• itmb[ i ] = 100 + i ; // mennyi lesz a
// tömbelemek értéke?
© Valkai Zoltán 2020 ÓE/KVK/MAI 6
ÉRTÉKADÁS
int itmb [ ] = { 1, 3, 5, 7, 9 }, m=0 ;
m = sizeof ( itmb ) ;
m=?
hány eleme van az itmb tömbnek?

#define N 20 // ezt miért szeretük?


int main(void)
{
int itmb[ N ] = {1, 4, 9, 16, 25};

return 0;
} MI történik, ha N értéke 2?
© Valkai Zoltán 2020 ÓE/KVK/MAI 7
HOGYAN HELYEZKEDNEK EL A
TÖMB ELEMEI a M-ban?

• egymás után deklarált változók egymás után a


M-ban; majd később

• tömb elemei azonos típusúak → a változókhoz


hasonlóan egymás után a M-ban

© Valkai Zoltán 2020 ÓE/KVK/MAI 8


ÍRÁS / OLVASÁS / FELÜLÍRÁS
• int itmb [ 5 ] = { 0 , 1 , 4 , 9 , 16 }, s = 0;
• itmb[ 1 ] = 2 ; // 1 → 2
• itmb[ 3 ] = 4 ; // 9 → 4

• s = itmb[ 0 ] + itmb[ 2 ] + itmb[ 4 ] ;

• itmb[ 4 ] = 10; itmb[ 2 ] = itmb[ 0 ] + itmb[ 4 ];

© Valkai Zoltán 2020 ÓE/KVK/MAI 9


KIÍRATÁS
• a tömb elemei egyedi értékű objektumok

• minden objektum azonos típusú

• Az objektumok csak egyesével irathatók ki

• ciklussal

© Valkai Zoltán 2020 ÓE/KVK/MAI 10


KIÍRATÁS
int main(void)
{
int itmb[ ] = { 10, 12, 14, 16, 18 }, i=0, m=0;
m = sizeof ( itmb ) / sizeof ( itmb [ 0 ] );
for ( i = 0; i < m; i++ )
printf ( ”%i\n”, itmb [ i ] );
return 0;
}

© Valkai Zoltán 2020 ÓE/KVK/MAI 11


KÖSZÖNÖM A FIGYELMET!

© Valkai Zoltán 2020 ÓE/KVK/MAI 12


PROGRAMOZÁS II.
3. Előadás

KARAKTERTÖMB a
C programozásban

© Valkai Zoltán 2020 ÓE/KVK/MAI 1


KARAKTERTÖMB TULAJDONSÁGAI C-ben
• string típus NINCS a C-ben
• char egyszerű adattípus a C-ben
• mindig 8 bites, azaz 1B-os
• előjeles vagy előjeltelen
• short, long
• minden betű → ASCII kód (American Standard
Code for Information Interchange)
• @ → 40H vagy 0x40
• 0 → 30H vagy 0x30
© Valkai Zoltán 2020 ÓE/KVK/MAI 2
HURRÁ!
• ezek szerint a karaktertömbben ASCII kódok
vannak

• lehetnek akár 8 bites adatok


megkülönböztetés?

• de mitől lesz karaktertömb a karaktertömb?

© Valkai Zoltán 2020 ÓE/KVK/MAI 3


MEKKORA MÉRETŰ
KARAKTERTÖMB KELL?

• Hello

• 5 betűből álló szó

• 5 ASCII kód biztosan kell

© Valkai Zoltán 2020 ÓE/KVK/MAI 4


DEKLARÁCIÓ
• típus azonosito_nev [ elemek_darabszama ] ;
• char ktb [ 50 ] ; // olyan, mint a tömbnél

• #define N 100
• char ktb [ N ] ; // mint a tömbnél

• értékadás nem kötelező, de lehetséges

© Valkai Zoltán 2020 ÓE/KVK/MAI 5


KARAKTERTÖMB MÉRETE
char ktb [ 50 ] ;
int m = 0;
m = sizeof ( ktb ) ;

Adatelem mérete : 1 B

tömb elemszáma

© Valkai Zoltán 2020 ÓE/KVK/MAI 6


KARAKTERTÖMB
Def.:
A karaktertömböt MINDIG egy 0 értékű bájt
zárja le 0, 0x00, 0000

A legkisebb méretű karaktertömb mérete 1B


Ebben nincs hasznos információ, csak 0 (nem ’0’)

Ettől lesz karaktertömb a karaktertömb a C-ben!


© Valkai Zoltán 2020 ÓE/KVK/MAI 7
ÉRTÉKADÁS
char ktb [ 10 ] = { ’H’, ’e’, ’l’, ’l’, ’o’ } ; //mennyi a többi
bájt értéke?

char ktb [ 3 ] = { ’H’, ’e’, ’l’, ’l’, ’o’ } ; // ez jó?


char ktb [ 3 ] = { ’H’, ’e’, ’l’, ’l’, ’o’, 0} ; // és ez?

char ktb [ 10] = {0x48, 0x65, 0x6C, 0x6C, 0c6F, 0 } ;


Ez borzalom vagy borz alom?!

© Valkai Zoltán 2020 ÓE/KVK/MAI 8


ÉRTÉKADÁS
char ktb [ 10];
ktb [ 0 ]= 072;
ktb [ 1 ]= 0101;
ktb [ 2 ]= 0108;
ktb [ 3 ]= 0108;
ktb [ 4 ]= 0111;
ktb [ 5 ]= 0000;

© Valkai Zoltán 2020 ÓE/KVK/MAI 9


ÉRTÉKADÁS
• Mindig számolgassunk?
• Pont mi?
• Tudunk mi számolni?
• Nem fogjuk elhibázni?
• Kell ez nekünk?
• Nincs valami egyszerűbb?

© Valkai Zoltán 2020 ÓE/KVK/MAI 10


ÉRTÉKADÁS
char ktb [ ] = ”Hello”;
int m=0;
m=sizeof ( ktb ) ;

char ktb [ ] = ”Hello”;


ktb [ 4 ] = 0 ;
No ez aztán pokol!
© Valkai Zoltán 2020 ÓE/KVK/MAI 11
HOZZÁFÉRÉS az egyes ELEMEKHEZ
Mennyi a ”Hello” szó bájtjainak összege?

int s = 0, i = 0; // miért?
char ktb [ ] = ”Hello”;
while ( ktb [ i ] !=0)
{
s = s + ktb [ i ] ;
i++;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 12
KIÍRATÁS
Karaktertömb kiírása a lezáró 0 értékű bájtig
történik

char ktb [ ] = ”A macska megeszi az egeret” ;


int i = 0 ;
i = printf ( ”%s\n” , ktb ) ;
i=?

© Valkai Zoltán 2020 ÓE/KVK/MAI 13


KIÍRATÁS
Karaktertömb kiírása a lezáró 0 értékű bájtig történik

char ktb [ ] = ”A macska megfogja az egeret” ;


int i = 0 ;
while ( ktb [ i ] != 0 ) // ktb [ i ]
{
printf (”%c”,ktb [ i ] ) ;
i ++ ;
}
printf ( ”\n” ) ;
© Valkai Zoltán 2020 ÓE/KVK/MAI 14
KARAKTER CSERE
Szóköz csere _-ra
char ktb [ ] = ”A macska megfogja az egeret” ;
int db = 0 , i = 0 ;
while ( ktb [ i ] )
{
if ( ktb [ i ] == 0x20 )
ktb [ i ] = ’_’ ; db ++ ;
i ++ ;
}
printf ( ”Cserek szama: %i\n” , db) ;
© Valkai Zoltán 2020 ÓE/KVK/MAI 15
KÖSZÖNÖM A FIGYELMET!

© Valkai Zoltán 2020 ÓE/KVK/MAI 16


PROGRAMOZÁS II.
4. Előadás

LINEÁRIS TÖMBÖK
TÖBBDIMENZIÓS
TÖMBÖK
©Valkai Zoltán 2020 ÓE/KVK/MAI 1
TÖMB ÁTADÁSA FÜGGVÉNYNEK
• A függvény deklarációban meg kell adni a
függvény bemenő paramétereinek
darabszámát
• Paraméterátadás a STACK-en ( LIFO ) keresztül
történik
• Az átadott paraméterekből egy ”másolat”
keletkezik

©Valkai Zoltán 2020 ÓE/KVK/MAI 2


LINEÁRIS TÖMB
• Véges számú elem
• Homogén (azonos típusú)

• Hivatkozás
– n egymást követő indexhalmaz
– Tömb elemei egymást követő memóriahelyeken

©Valkai Zoltán 2020 ÓE/KVK/MAI 3


LINEÁRIS TÖMB MŰVELETEI
• Bejárás
• Keresés
– szekvenciális ( lineáris )
– bináris
• Beszúrás
• Törlés
• Rendezés
• Összefésülés
©Valkai Zoltán 2020 ÓE/KVK/MAI 4
BEJÁRÁS
• Mi tudunk? Hogyan végezhetjük el?
• Véges számú elemünk van
• Első elem indexe 0.
• Utolsó elem indexe: elemek száma -1

• Ciklussal végiglépünk az indexekkel a tömbön

• Utolsó adat után is lesz valami a memóriában?


• Ezek azonban nem a tömbhöz tartozó adatok
• Fordítási hibajelzés nincs

©Valkai Zoltán 2020 ÓE/KVK/MAI 5


BEJÁRÁS
Mennyi a tömb elemeinek összege?
int main( void )
{
short int tmb [ ] = { 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 };
int m = 0, s = 0, i = 0 ;
m = sizeof ( tmb ) / sizeof ( tmb [ 0 ] ) ;
while ( i < m )
{
s = s + tmb [ i ] ;
i ++ ;
}
printf( "A tomb elemeinek osszege: %i\n" , s );
return 0;
}

©Valkai Zoltán 2020 ÓE/KVK/MAI 6


LINEÁRIS KERESÉS
• Hogyan?
• Adott értékű elem megtalálható-e a tömbben?
• A bejáráshoz hasonlóan végiglépünk az
elemeken az elejétől a végéig
• Minden egyes elemet összehasonlítunk a
keresett értékkel
• Egyezés esetén tároljuk az információt
• Mivel minden egyes elemet megvizsgálunk, a
tömbnek nem kell rendezettnek lennie (de
lehet akár rendezett is)
©Valkai Zoltán 2020 ÓE/KVK/MAI 7
LINEÁRIS KERESÉS
Megtalálható-e egy adott értékű elem a tömbben ? Hányszor ?
#define KX 12 // a keresett elem
int main()
{
short int tmb [] = { 12, 10, 24, 20, 14, 16, 12, 15, 18, 21, 22, 12 } ;
int m = 0 , i = 0 , van_e = 0 , db = 0 ;
m = sizeof ( tmb ) / sizeof ( tmb [ 0 ] );
while ( m > 0)
{
if ( tmb [ i ] == KX )
{
db ++ ; van_e = 1 ;
}
m -- ; i ++ ;
}
if ( van_e )
printf( "A keresett elem %i * fordul elo a tombben\n" , db );
return 0;
}
©Valkai Zoltán 2020 ÓE/KVK/MAI 8
BESZÚRÁS
Hogyan? Hova?
Új elem beillesztése a tömbbe
Rendezetlen tömb esetén bárhova kerülhet
Lesz helye? Mikor végezhető el?
Rendezett tömb esetén meg kell keresni az új elem
helyét
Hogyan tartsuk fenn a rendezettséget?
Elemek átmozgatása a vége felé
Az „üres helyre” az új elem beillesztése

©Valkai Zoltán 2020 ÓE/KVK/MAI 9


BESZÚRÁS
int main()
{
short int tmb [15] = { 12, 20, 24, 30, 34, 36, 42, 45, 48, 51, 52, 62 } ;
int i = 0 , uj = 10 , m = sizeof ( tmb ) / sizeof ( tmb [ 0 ] ) , uji ;
while ( tmb [ i ] < uj && tmb [ i+1 ] >= tmb [ i ] )
{
if ( tmb [ i ] >= uj ) break;
i ++ ;
}
uji = i ;
while ( m – i – 1 )
{
tmb [ m - 1 ] = tmb [ m - 2 ] ;
m -- ;
}
if ( tmb [ uji ] < uj ) tmb [ uji + 1 ] = uj ;
else tmb [ uji ] = uj ;
m = sizeof ( tmb ) / sizeof ( tmb [ 0 ] ) ; i = 0 ;
while ( m )
{
printf ( "%10i", tmb [ i ] ) ; i ++ ; m -- ;
}
printf ( "\n\n" ) ;
return 0 ;
}
©Valkai Zoltán 2020 ÓE/KVK/MAI 10
TÖRLÉS
• Adott értékű elem törlése a tömbből
• Hogyan?
• A tömbünk rendezett vagy rendezetlen?
• Biztosan van ilyen értékű elem a tömbben?
• Mi történjen a törölt elem helyével?
• Rendezett tömb esetén
• Keresés
• Mögötte lévő elemek mozgatása a tömb eleje felé
• Utolsó elem törlése

©Valkai Zoltán 2020 ÓE/KVK/MAI 11


TÖRLÉS
int main()
{
short int tmb [15] = { 12, 20, 24, 30, 34, 36, 42, 45, 48, 51, 52, 55, 62 } ;
int i = 0 , torlendo = 62, m = sizeof ( tmb ) / sizeof ( tmb[0] ) ;
while( tmb[i] != torlendo && tmb[i+1] >= tmb[i] )
i++;
if ( tmb[ i ] == torlendo )
{ while (m-i-1)
{ tmb[ i ] = tmb[ i+1 ] ; i++ ; }
}
m = sizeof( tmb ) / sizeof( tmb[0] ); i = 0;
while( m )
{
printf( "%10i", tmb[ i ]) ;
i++ ; m--;
}
return 0;
}
©Valkai Zoltán 2020 ÓE/KVK/MAI 12
RENDEZÉS
A tömb eredetileg
rendezetlen,
vagy nem tudunk semmit róla
Eric Schmidt vs. Obama (2007.)
Egymillió egész rendezése
1955 cserélő algoritmus, cserével történő válogatás
Első, kifejezetten számítógépre készített algoritmus
Páronként történő összehasonlítás
elemek cseréje, ha a sorrend nem megfelelő

©Valkai Zoltán 2020 ÓE/KVK/MAI 13


RENDEZÉS
5 3 8 1 2 7 4 3 1 2 5 4 7 8
3 5 8 1 2 7 4 1 3 2 5 4 7 8
3 5 8 1 2 7 4 1 2 3 5 4 7 8
3 5 1 8 2 7 4 1 2 3 5 4 7 8
3 5 1 2 8 7 4 1 2 3 4 5 7 8
3 5 1 2 7 8 4 1 2 3 4 5 7 8
3 5 1 2 7 4 8 1 2 3 4 5 7 8
3 5 1 2 7 4 8 1 2 3 4 5 7 8
3 1 5 2 7 4 8 1 2 3 4 5 7 8
3 1 2 5 7 4 8 1 2 3 4 5 7 8
3 1 2 5 7 4 8 1 2 3 4 5 7 8
3 1 2 5 4 7 8 1 2 3 4 5 7 8

©Valkai Zoltán 2020 ÓE/KVK/MAI 14


RENDEZÉS
int main()
{
short int tmb [ ]={ 62,30,55,20,34,42,36,42,45,28,36,52,51,24,12 };
int m = sizeof(tmb) / sizeof(tmb[0]), i = 0, j = m-1, tmp, x = (m-1)*(m-1);
while ( x )
{
if ( tmb[ i+1 ] < tmb[ i ] )
{ tmp = tmb[ i ] ; tmb[ i ] = tmb [ i+1 ] ; tmb [ i+1 ] = tmp ; }
j -- ; i ++ ; x -- ;
if( !j ) { j = m-1 ; i = 0 ; x++ ; }
}
m = sizeof ( tmb ) / sizeof (tmb[ 0 ] ) ; i = 0;
while ( m )
{
printf( "%8i", tmb [ i ] ) ; i++ ; m-- ;
}
return 0 ;
}
©Valkai Zoltán 2020 ÓE/KVK/MAI 15
2D TÖMB
int main(void)
{
int j, k;
int t2 [ 3 ][ 2 ] = { 200, 201, 210, 211, 220, 221 };
// { {200 , 201} , {210 , 211 } , { 220, 221 } }
for (j = 0; j < 3; j ++, printf("\n") )
for ( k = 0; k < 2; k++ )
printf ( "%02i ", t2[ j ] [ k ] – 200 ) ;
return 0;
}

©Valkai Zoltán 2020 ÓE/KVK/MAI 16


TRANSZPONÁLÁS
#define M 2
#define N 4
int main ( )
{
short int tmb1 [M] [N] = { 2, 4, 6, 3, 5, 7, 8, 9 } ;
short int tmb2 [N] [M] = { 0 } ;
int tmp, i, j ;
for (i = 0; i < M; i++ )
for(j=0; j<N; j++)
tmb2 [ j ] [ i ] = tmb1 [ i ] [ j ] ;

for (i = 0; i < N; i++, printf("\n") )


for (j = 0; j < M; j++ )
printf ("%10i",tmb2 [ i ] [ j ] ) ;

printf("%c%c", 0xa, 0xa);


return 0 ;
}
©Valkai Zoltán 2020 ÓE/KVK/MAI 17
ÖSSZEADÁS
#define M 2
#define N 4
int main ( )
{
short int tb1 [M] [N] = { 2, 4, 6, 3, 5, 7, 8, 9 } ;
short int tb2 [M] [N] = { 1, 2, 4, 3, 6, 7, 9, 8 } ;
short int tb3 [M] [N] = { 0 } ;
int tmp, i, j ;
for (i = 0; i < M; i++ )
for (j = 0; j < N; j++ )
tb3 [ i ] [ j ] = tb1 [ i ] [ j ] + tb2 [ i ] [ j ] ;

for ( i = 0; i < M; i++, printf("\n") )


for ( j = 0; j < N; j++ )
printf ( "%10i",tb3 [ i ] [ j ] ) ;

printf("%c%c", 0xa, 0xa ) ;


return 0;
}
©Valkai Zoltán 2020 ÓE/KVK/MAI 18
SZORZÁS
#define M1 3
#define N1 3
#define M2 3
#define N2 3
int main()
{
if ((( M1==N2 ) || ( M2==N1 )) || (( M1==M2 ) && (N1 == N2 )))
{
int tb1 [ M1 ] [ N1 ] = { 2,4,6, 3,5,7, 8,9,0 } ;
int tb2 [ M2 ] [ N2 ] = { 1,2,4, 3,6,7, 9,8,0 } ;
int tb3 [ M1 ] [ M1 ] = { 0 } ;
int tmp, i, j, k ;

©Valkai Zoltán 2020 ÓE/KVK/MAI 19


SZORZÁS
for ( i = 0; i < M1; i++ )
{
for ( tmp=j=0; j < M2; j++ )
{
for(k=0; k<M2; k++)
tmp = tmp + tb1 [ i ] [ k ] * tb2 [ k ] [ j ];
tb3 [ i ] [ j ] = tmp ;
tmp = 0 ;
}
}
for ( i = 0; i < M1; i++ )
for ( j = 0; j < M1; j++ )
printf ("%10i",tb3 [ i ] [ j ] ) ;
printf("\n\n");
}
else
printf("NEM SZOROZHATOK OSSZE\n” ) ;
return 0;
}
©Valkai Zoltán 2020 ÓE/KVK/MAI 20
0-TÓL KÜLÖNBÖZÓ ELEMEK DB
#define M 5
#define N 5
int main()
{
int tmb[M][N] = { 1,2,0,3,3,2,0,2,4,6,7,0,5,-1,1,4,1,0,2,-2,-1,4,2,0,3 } ;
int i, j, db = 0;
for ( i = 0; i < M; i++ )
for ( j = 0; j < N; j++ )
if ( tmb [ i ] [ j ] )
db ++ ;
printf("A 0-tol kulonbozo erteku elemek darabszama: %i\n", db);
return 0;
}
©Valkai Zoltán 2020 ÓE/KVK/MAI 21
FŐÁTLÓBELI ELEMEK ÖSSZEGE
#define M 5
#define N 5
int main ()
{
int tmb [ M ] [ N ] = { 1,2,0,3,3, 2,0,2,4,6, 7,0,5,-1,1, 4,1,0,2,-2, -
1,4,2,0,3 };
int i, j, sum = 0 ;
for ( i = 0; i < M; i++ )
sum = sum + tmb [ i ] [ i ] ;
printf("A foatlobeli elemek osszege: %i\n", sum ) ;
return 0;
}
//Szorzat esetén?
©Valkai Zoltán 2020 ÓE/KVK/MAI 22
PÁRHUZAMOS TÖMBÖK
• Különböző típusú, de azonos számú sort
tartalmazó tömbök
• A egyes tömbök azonos típusú adatokat
tartalmaznak
• A tömbök azonos indexű adatai tartoznak
össze

©Valkai Zoltán 2020 ÓE/KVK/MAI 23


PÁRHUZAMOS TÖMBÖK
Személyek adatai:

char v_nev [ 100 ] [ 20 ] ;


char k_nev [ 100 ] [ 30 ] ;
int szul_ev [ 100 ] ;
char szul_hely [ 100 ] [ 25 ] ;
int szem_szam [ 100 ] ;
int taj_szam [ 100 ] ;
©Valkai Zoltán 2020 ÓE/KVK/MAI 24
RITKA MÁTRIXOK

┌ ┐ ┌ ┐
│ 1 0 0 0 0 │ │ 3 3 0 0 0 │
│ 0 5 0 0 0 │ │ 1 2 4 0 0 │
│ 0 0 2 0 0 │ │ 0 3 1 5 0 │
│ 0 0 0 3 0 │ │ 0 0 5 5 6 │
│ 0 0 0 0 7 │ │ 0 0 0 7 5 │
└ ┘ └ ┘

©Valkai Zoltán 2020 ÓE/KVK/MAI 25


RITKA MÁTRIXOK

┌ ┐ ┌ ┐
│ 1 7 2 3 1 │ │ 3 0 0 0 0 │
│ 0 5 1 4 2 │ │ 1 2 0 0 0 │
│ 0 0 2 5 1 │ │ 1 3 1 0 0 │
│ 0 0 0 3 0 │ │ 2 4 5 5 0 │
│ 0 0 0 0 7 │ │ 1 3 6 7 5 │
└ ┘ └ ┘

©Valkai Zoltán 2020 ÓE/KVK/MAI 26


3D-s TÖMB

• Az egy dimenziós tömb egyedi elemekből áll

• A két dimenziós tömb egydimenziós


tömbökből áll

• A három dimenziós tömb kétdimenziós


tömbökből hozható létre
©Valkai Zoltán 2020 ÓE/KVK/MAI 27
2,0,0 2,0,1 2,0,2 2,0,3 2,0,4 2,0,5 2,0,6 2,0,7 2,0,8 2,0,9

2,1,0

2,2,0

2,3,0

2,4,0 2,4,9

1,0,0 1,0,1 1,0,2 1,0,3 1,0,4 1,0,5 1,0,6 1,0,7 1,0,8 1,0,9

1,1,0

1,2,0

1,3,0

1,4,0 1,4,9

0,0,0 0,0,1 0,0,2 0,0,3 0,0,4 0,0,5 0,0,6 0,0,7 0,0,8 0,0,9

0,1,0

0,2,0

0,3,0

0,4,0 0,4,9

©Valkai Zoltán 2020 ÓE/KVK/MAI 28


3D-s TÖMB
int main (void)
{
int i, j, k ;
int t3 [ 4 ] [ 3 ] [ 2 ] =
{ 3000, 3001, 3010, 3011, 3020, 3021, 3100, 3101, 3110, 3111,
3120, 3121, 3200, 3201, 3210, 3211, 3220, 3221, 3300, 3301,
3310, 3311, 3320, 3321 };
for ( i=0; i<4; i++, printf("\n"))
for (j = 0; j < 3; j++, printf("\n"))
for ( k = 0; k < 2; k++)
printf ( "%03i ",t3[ i ] [ j ] [ k ] – 3000 ) ;
printf("\n\n");
return 0;
}

©Valkai Zoltán 2020 ÓE/KVK/MAI 29


GRÁFOK
• Irányított gráf
– Csomópontok vagy csúcsok
– Utak vagy élek (honnan-hova)

• problémák
– Utazó ügynök
– Legrövidebb út hossza

©Valkai Zoltán 2020 ÓE/KVK/MAI 30


GRÁFOK
• Csúcsok halmaza: { 1, 2, 3, 4, 5, 6, 7 }
• Élek halmaza: {(1,2), (2,3), (4,1)}

– Egy számítógépes hálózat is modellezhető gráffal


– Melyik a legrövidebb út?
– Hányféle lehetséges út van 2 csomópont között?
– Mely utak redundánsak és melyek nem?

©Valkai Zoltán 2020 ÓE/KVK/MAI 31


KÖSZÖNÖM A FIGYELMET!

©Valkai Zoltán 2020 ÓE/KVK/MAI 32


PROGRAMOZÁS II.
5. Előadás

POINTEREK I.
( MUTATÓK )

© Valkai Zoltán 2020 ÓE/KVK/MAI 1


TULAJDONSÁGAI
• változó: érték, típus, memória, műveletek,
operátorok
• POINTER ↔ mutatópálca
• „speciális változó”
• Nem értéket, hanem MEMÓRIACÍMET tárol
• A memóriacím is egy numerikus érték
• Típusa van
• Operatív memóriában
• Az objektum kezdőcímét ( a legalacsonyabb
címet)
© Valkai Zoltán 2020 ÓE/KVK/MAI 2
DEKLARÁCIÓ
• Mi határozza meg a típusát?

• Annak az objektumnak a típusa, amire mutatni


szeretnénk ( lehet trükközni )

• Honnan lehet tudni, hogy mi változó és mi


pointer?

© Valkai Zoltán 2020 ÓE/KVK/MAI 3


DEKLARÁCIÓ
• Speciális operátorok:
– Címképző
– Indirekciós

int * ip ; // egészre mutató pointer


char * cp ; // char típusra mutató pointer
double * dp ; // double típusra mutató

© Valkai Zoltán 2020 ÓE/KVK/MAI 4


POINTER MÉRETE
mi = sizeof ( ip ) ; // int típusú pointer
mc = sizeof ( cp ) ; // char típusú pointer
• Operációs rendszer
• x86 → 32 bit = 4 B
• 64 bites → 64 bit = 8 B
• Független az argumentumban szereplő
objektum típusától

© Valkai Zoltán 2020 ÓE/KVK/MAI 5


POINTER ÉRTÉKADÁS
int x = 5 ;
int * xp ;
xp = & x ;
char k = ’#’ ;
char * kp ;
kp = & k ;
char kt [ ] = ”Kando” ;
char * kp ;
kp = kt ;
char * kp = & kt [ 0 ] ;
© Valkai Zoltán 2020 ÓE/KVK/MAI 6
POINTER MŰVELETEK
= értékadás pnt1 = pnt2 ;
Csak azonos típusúak auto típuskonverzió
++ / -- pnt = pnt +/- sizeof ( type of pnt)
1 Bájt
char pnt

pnt + 1

pnt + 2
© Valkai Zoltán 2020 ÓE/KVK/MAI 7
POINTER MŰVELETEK
++ / -- pnt = pnt +/- sizeof ( type of pnt)
1 Bájt 4 Bájt
int pnt

pnt+1

pnt+2

© Valkai Zoltán 2020 ÓE/KVK/MAI 8


POINTER MŰVELETEK
+/- N pnt = pnt +/- N * sizeof ( type of pnt)
2 Bájt
short int pnt

pnt+3

pnt+7

pnt+10

© Valkai Zoltán 2020 ÓE/KVK/MAI 9


POINTER MŰVELETEK
- ( negatív előjel ) ?
pnt1 + pnt2 ?
pnt1 - pnt2

pnt1 * pnt2 ?
pnt1 / pnt2 ?
pnt1 % pnt2 ?
relációs
Bit ?
logikai ( majd… )
© Valkai Zoltán 2020 ÓE/KVK/MAI 10
POINTER HIVATKOZÁS
int x = 2 , y = 8 ;
int *pnt ;
pnt = & x ;
*pnt = y ;
*pnt = 11 ;
Melyik a pointer?
pnt
*pnt

© Valkai Zoltán 2020 ÓE/KVK/MAI 11


POINTER HIVATKOZÁS
Mennyi lesz két változó összege pointerrel?

int x = 2, y = 3, z = 0 ;
int *pnt ;
pnt = & x ;
z = z + *pnt ;
pnt = & y ;
z = z + *pnt ;
pnt = & z ;
printf(”x + y = %i\n”, z);
printf(”x + y = %i\n”, *pnt);
© Valkai Zoltán 2020 ÓE/KVK/MAI 12
VOID TÍPUSÚ MUTATÓK
• A pointer típusát a deklaráció határozza meg
• A méretét az operációs rendszer bitszáma
• A void típus bármilyenné konvertálható cast-
olással
• Bármilyen típusú pointer konvertálható void
típusúvá
• A cast-olt típusú mutató műveletei már a
típusnak megfelelően történnek
© Valkai Zoltán 2020 ÓE/KVK/MAI 13
TÖBBSZÖRÖS INDIREKCIÓ
int x = 666, * xp, ** xpp;
xp = & x;
xpp = & xp ;
** xpp = 101 ;

int x = 169, * xp, ** xpp, ***xppp;


xp = & x ,
xpp = & xp ;
xppp = & xpp ;
***xppp = 961 ;
© Valkai Zoltán 2020 ÓE/KVK/MAI 14
MUTATÓTÖMB(ÖK)

PIROS

ZÖLD

TÖK

MAKK

© Valkai Zoltán 2020 ÓE/KVK/MAI 15


MUTATÓTÖMB(ÖK)
int main()
{
int i , m ;
char *szinek [ ] = { "piros","zold","tok","makk" } ;
m = sizeof ( szinek ) / sizeof ( szinek[0] ) ;
char **szinp [ m ] ;
char ***szinpp ;
for ( i = 0 ; i < m ; i ++ )
szinp [ i ] = & szinek [ i ] ;

© Valkai Zoltán 2020 ÓE/KVK/MAI 16


MUTATÓTÖMB(ÖK)

szinpp = &szinp ;

for ( i = 0 ; i < m ; i ++ )
{
printf ( "%s\n" , **szinpp ) ;
szinpp ++ ;
}
return 0 ;
}

© Valkai Zoltán 2020 ÓE/KVK/MAI 17


KÖSZÖNÖM A FIGYELMET!

© Valkai Zoltán 2020 ÓE/KVK/MAI 18


PROGRAMOZÁS II.
6. Előadás

POINTEREK II.

© Valkai Zoltán 2020 ÓE/KVK/MAI 1


POINTER ÁTADÁS FÜGGVÉNYNEK
• A függvény deklarációban kell megadni a
bemenő paraméter(ek) típusát
• A függvény meghívásakor kell aktualizálni a
bemenő paraméter(ek)t
• Miért adunk át pointert?
• Mi az előnye?

© Valkai Zoltán 2020 ÓE/KVK/MAI 2


POINTER ÁTADÁS FÜGGVÉNYNEK
Mennyi a tömbben lévő bájtok összege?
int fv1 ( char *cp ) ;
int main ( void )
{
int y = 0 ;
char kt [ ] = ”Ekszer a MUSZER!” ;
y = fv1 ( kt ) ; // *cp ↔ kt ?
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 3
POINTER ÁTADÁS FÜGGVÉNYNEK
int fv1( char *cp )
{
int w = 0 ;
while ( *cp ) //*cp != 0
{
w = w + * cp ;
cp ++ ; //
}
return w ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 4
POINTER ÁTADÁS FÜGGVÉNYNEK
Mennyi a tömbben lévő egészek összege?
int fv2 ( int *ip , int db ) ;
int main ( void )
{
int y = 0 , m = 0 ;
int it [ ] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 } ;
m = sizeof ( it ) / sizeof ( it [ 0 ] ) ;
y = fv2 ( it , m ) ;
return 0;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 5
POINTER ÁTADÁS FÜGGVÉNYNEK
int fv2 ( int *ip , int db )
{
int w = 0 ;
while ( db )
{
w = w + * ip ;
ip ++ ; //
db--;
}
return w ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 6
POINTER ÁTADÁS FÜGGVÉNYNEK
Mennyi a tömbben lévő legnagyobb érték?
int fv2 ( int *ip , int db ) ;
int main ( void )
{
int it [ ] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 } ;
int y = 0, * ip = it , m = 0 ;
m = sizeof ( it ) / sizeof ( it [ 0 ] ) ;
y = fv2 ( ip , m ) ;
return 0;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 7
POINTER ÁTADÁS FÜGGVÉNYNEK
int fv2 ( int *ip , int db )
{
int max = *ip ;
while ( db )
{
if ( *ip > max ) max = * ip ;
ip ++ ;
db--;
}
return max ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 8
POINTER ÁT- és VISSZAADÁSA
//Melyik a legnagyobb értékű ASCII kódu betű?
char *fv3 ( char *cp ) ;
int main ( void )
{
char * wp ;
char kt [ ] = ”Ekszer a MUSZER!” ;
wp = fv3 ( kt ) ;
printf ( ”A legnagyobb kodu betu %c”, *wp ) ;
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 9
POINTER ÁT- és VISSZAADÁSA
char *fv3 ( char *cp )
{
char *wp , cmax = 0x20;
while ( *cp )
{
if ( *cp > cmax ) wp = cp ;
cp ++ ;
}
return wp ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 10
TÖBB POINTER ÁTADÁSA
Két rendezetlen tömb adatainak összefésülése

© Valkai Zoltán 2020 ÓE/KVK/MAI 11


TÖBB POINTER ÁTADÁSA
#define M3 100
int *min ( int *ip , int n ) ;
int fesul ( int *ip1, int n1, int *ip2, int n2, int *ip3, int n3 ) ;
int main ( )
{
int m1=0, m2=0, w ;
int t1 [ ] = { 35, 10, 40, 30, 20, 15, 25, 5, 45 } ;
m1 = sizeof ( t1 ) / sizeof ( t1 [ 0 ] ) ;
int t2 [ ] = { 27, 24, 12, 30, 21, 15, 18 } ;
m2 = sizeof ( t2 ) / sizeof( t2 [ 0 ] ) ;
int t3 [ M3 ] = { 0 } ;
w = fesul ( t1, m1, t2, m2, t3, M3 ) ;
printf ("%i\n", w ) ;
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 12
TÖBB POINTER ÁTADÁSA
int fesul(int *ip1, int n1, int *ip2, int n2, int *ip3, int n3)
{
int i1, i2, i3=0, w = 0;
int *tp1 = ip1, *tp2 = ip2, *tp3 = ip3, *tmp1, *tmp2;
while ( w < (n1+n2) )
{
tmp1 = min ( tp1 , n1 ) ;
tmp2 = min ( tp2 , n2 ) ;
if ( *tmp1 < *tmp2 ) { *tp3 = *tmp1; tp3++; *tmp1 = 0; }
else { *tp3 = *tmp2; tp3++; *tmp2 = 0 ; }
w++;
tp1 = ip1, tp2 = ip2;
}

for ( i3 = 0 ; i3 < M3 ; i3++ )


printf( "%8i", *ip3++ ) ;
return w;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 13
TÖBB POINTER ÁTADÁSA
int *min (int *ip , int n)
{
int *mincim, *tcim = ip, m = 2e9 , i;
for ( i = 0 ; i < n ; i++ , tcim ++)
{
if ( (*tcim < m ) && ( *tcim > 0 ) )
{
m = *tcim ;
mincim = tcim ;
}
}
return mincim;
}

© Valkai Zoltán 2020 ÓE/KVK/MAI 14


Tömb átadása [ ]
int s = 0, tmb [ ] = { 11, 13, 17, 19};
s = fv (tmb [֍], sizeof ( tmb ) / sizeof ( tmb[ 0 ]) ) ;

Deklarációs probléma is lesz:

int fv (int tomb [ ], int n ) ; expected int *


int fv ( int *tomb [ 5 ], int n ) ; expected int *

int fv(int tomb[5], int n);


s = fv(&tmb[5],sizeof(tmb)/sizeof(tmb[0]));

NE
© Valkai Zoltán 2020 ÓE/KVK/MAI 15
TÖBB POINTER VISSZAADÁSA
• A függvénynek csak egy visszatérési értéke
lehet
– Érték
– Cím ( akár többszörösen indirekt is lehet )
• Különböző típusú adatokat, de ez nem
automatikus, nekünk is dolgozni kell rajta
(következő előadásban )

© Valkai Zoltán 2020 ÓE/KVK/MAI 16


FORMÁTUMOZOTT BEOLVASÁS
• Miért van rá szükség?
• A programot különböző paraméterekkel
akarjuk futtatni
• A deklarált változóknak történő értékadás fix,
nem módosítható
• Futás közben történő adatbeolvasás

© Valkai Zoltán 2020 ÓE/KVK/MAI 17


FORMÁTUMOZOTT BEOLVASÁS
• < stdio.h >
• printf → formátumozott kiírás

• scanf → formátumozott beolvasás


• ENTER-ig olvas
• Kiíratni nem lehet vele!
• (,); argumentum
• Formátum megadás printf-hez hasonló
• Vezérlő karakter TILOS!
© Valkai Zoltán 2020 ÓE/KVK/MAI 18
FORMÁTUMOZOTT BEOLVASÁS
int i, m;
printf("Kerek egy egesz erteket: ");
m=scanf("%i",&i);
printf("Ertek: %i\n",i);
printf("Beolvasva: %i blokk\n",m);
Sikeres => m=1
Sikertelen => m=0
Mezőszélesség adott => annyi számjegy, m=1
Nem számjegyre leáll 45abc =>45
Tömböt csak egyesével, elemenként
Szeparátor formátumnak egyeznie kell 
© Valkai Zoltán 2020 ÓE/KVK/MAI 19
FORMÁTUMOZOTT BEOLVASÁS
int i, m ;
char kt [ 20 ] ;
printf ( "Kerek egy stringet: " ) ;
m = scanf ( "%s", kt ) ;
printf ( "A string: %s\n", kt ) ;
printf("Beolvasva: %i blokk\n",m);
Mezőszélesség esetén sizeof(kt)-1
Számjegyet is beolvas

© Valkai Zoltán 2020 ÓE/KVK/MAI 20


FORMÁTUMOZOTT BEOLVASÁS
int i, m;
printf("Kerek egy hexa erteket: ");
m = scanf ( "%X" , &i ); // x X
printf("Ertek: %X\n",i);
printf("Beolvasva: %i blokk\n",m);

Megengedett: 0…9 a…f


többire leáll a beolvasás

© Valkai Zoltán 2020 ÓE/KVK/MAI 21


FORMÁTUMOZOTT BEOLVASÁS
int m;
float f;
printf("Kerek egy lebegopontos erteket: ");
m=scanf("%f",&f);
printf("Ertek: %f\n",f);
printf("Beolvasva: %i blokk\n",m);
Megengedett: 0…9 . (decimális pont )
Nem decimális pontra, nem számjegyre leáll
Mezőszélességet felülírja
© Valkai Zoltán 2020 ÓE/KVK/MAI 22
KÖSZÖNÖM A FIGYELMET!

Következik: összetett adatszerkezetek

© Valkai Zoltán 2020 ÓE/KVK/MAI 23


PROGRAMOZÁS II.
7. Előadás

ÖSSZETETT
ADATSZERKEZETEK

© Valkai Zoltán 2020 ÓE/KVK/MAI 1


ADATOK, ADATSZERKEZET
• Változó
• Alaptípus
• Alaptípus előre definiált a nyelvben
• Ezért csak a deklarációra van szükség
• Tömb összetett, de alaptípusú elemekből áll
• Jól számítható méretű memóriaterületet foglal
• Nem lehetnek különböző típusú összetartozó
adatok?
• Párhuzamos tömbök
© Valkai Zoltán 2020 ÓE/KVK/MAI 2
STRUKTÚRA
• Def.:
• Különböző típusú, de logikailag összetartozó
adatokat tartalmazó adatszerkezet
• Miért jó?
• Az alaptípusok felhasználásával tetszőleges
adatszerkezet létrehozható
– Saját
– Előre definiált ( <*.h> )

© Valkai Zoltán 2020 ÓE/KVK/MAI 3


STRUKTÚRA DEFINÍCIÓ
• Miért kell?
– Nem olyan, mint az alaptípusok
– A szerkezetét, az adatok típusát, méretét mi
határozzuk meg
• Hogyan?
• Hol kell ennek lennie?
• main()-en kívül
• függvényben is használni akarjuk

© Valkai Zoltán 2020 ÓE/KVK/MAI 4


STRUKTÚRA DEFINÍCIÓ
•Milyen adatokat, milyen formában akarunk
tárolni?
•Egyszerű adatszerkezet
•Bolt
•pénztáros száma // struct egyik eleme a mező
•pénztáros neve // ez is egy mező

struct penztarosok // deklaráció hivatkozás


{ int penztaros_szama ;
char penztaros_neve [ 51 ];
};
© Valkai Zoltán 2020 ÓE/KVK/MAI 5
STRUKTÚRA DEFINÍCIÓ
• Mire lesz / lehet ez majd jó?

• Forgalom ellenőrzése
• Pénztári elszámolás (nap végén)
• Munkaidő nyilvántartás
• Napi kiszolgált vásárlók száma
• stb.

© Valkai Zoltán 2020 ÓE/KVK/MAI 6


STRUKTÚRA DEFINÍCIÓ
Egy bonyolultabb adatszerkezet

struct repulo_jaratok //ezen a néven


//tudunk majd rá hivatkozni
{
char jaratszam [ 11 ] ;
char ind_all [ 4 ] ;
unsigned short int ind_ido ;
char cel_all [ 4 ] ;
unsigned short int erk_ido ;
};
© Valkai Zoltán 2020 ÓE/KVK/MAI 7
DEFINÍCIÓ
• Mit tartalmazhat?

• Bármilyen alaptípust

• typedef kulcsszóval → saját típus

• Másik struktúrát?

• Saját magát?
© Valkai Zoltán 2020 ÓE/KVK/MAI 8
DEKLARÁCIÓ
• Hol?
• Hogyan?

int x ;
struct repulo_jaratok rep_jarat ;
A rep_jarat azonosítóval tudunk majd rá
hivatkozni
• Változónak, tömbnek a deklarációban is lehet
értéket adni
© Valkai Zoltán 2020 ÓE/KVK/MAI 9
DEKLARÁCIÓ
a definíció után közvetlenül, ha a main()-ben van

struct repulo_jaratok
{
char jaratszam [ 11 ] ;
char ind_all [ 4 ] ;
unsigned short int ind_ido ;
char cel_all [ 4 ] ;
unsigned short int erk_ido ;
} rep_jarat ;
© Valkai Zoltán 2020 ÓE/KVK/MAI 10
DEKLARÁCIÓ + KEZDŐ ÉRTÉKADÁS
struct repulo_jaratok rep_jarat =
{
"LH1677",
"BUD",
1255,
"MUC",
1410
};
© Valkai Zoltán 2020 ÓE/KVK/MAI 11
MÉRETE
struct repulo_jaratok {
char jaratszam [ 11 ] ;
char ind_all [ 4 ] ;
unsigned short int ind_ido ;
char cel_all [ 4 ] ;
unsigned short int erk_ido ;
} rep_jarat ;
int m = sizeof ( rep_jarat ) ;
11 + 4 + 2 + 4 + 2 = 23
-fpack-struct
11 + 4 + 2 + 4 + 2 → 24
© Valkai Zoltán 2020 ÓE/KVK/MAI 12
HIVATKOZÁS
.
struct repulo_jaratok {
char jaratszam [ 11 ] ;
char ind_all [ 4 ] ;
unsigned short int ind_ido ;
char cel_all [ 4 ] ;
unsigned short int erk_ido ;
} rep_jarat ;
rep_jarat . jaratszam
rep_jarat . ind_all
rep_jarat . ind_ido
© Valkai Zoltán 2020 ÓE/KVK/MAI 13
ÉRTÉKADÁS
rep_jarat . ind_ido = 1255

rep_jarat . ind_all = ?
char ind_all [ 4 ] ;
<string.h>
strcpy ( rj . ind_all , "BUD" ) ;
strcpy ( rj . jaratszam , "LH1677" ) ;

© Valkai Zoltán 2020 ÓE/KVK/MAI 14


STRUKT A M-ban
• Az adatmezők általában sorfolytonosan
• DE
– Adattárolás (int, short int)
– Páros címen kell kezdődnie
• Több, ugyanolyan struktúra esetén → fordító
• Az adatok pontos helye kérdéses, de
• Cím szerint is tudunk rá hivatkozni

© Valkai Zoltán 2020 ÓE/KVK/MAI 15


MŰVELETEK
• A struktúrával nem tudunk műveletet végezni

• Különböző típusú, jellegű adatokat tartalmaz

• A struktúra egyes mezői hordozzák az


információt

• → csak a mezők
© Valkai Zoltán 2020 ÓE/KVK/MAI 16
KIÍRÁS
• Különböző típusú adatok a struktúrában

• Általános kiíró formátum nincs a C-ben

• Csak mezőnként

• A struktúra egyes mezői a mező formátumával


összeegyeztethető formátumstring-gel
© Valkai Zoltán 2020 ÓE/KVK/MAI 17
FELADAT
Könyvtár nyilvántartás

Szerző
Könyv címe
Kiadás éve
Ár
Kölcsönözhető-e?

© Valkai Zoltán 2020 ÓE/KVK/MAI 18


FELADAT
#include <string.h>
struct book
{
char szerzo [ 20 ]; char cim [ 40 ];
int ev ; int ar; int kikolcsonzo;//-1: konyvtarban
};
int main()
{
char szerzokeres[20] = "Kernigham";
struct book b01={"Kernighan","A C programozasi nyelv",1982,254,0};
struct book b02={"Kernighan","A C programozasi nyelv",1982,254,101};
struct book b03={"Wirth","Algoritmusok+adatstrukturak",1974,74,0};
struct book b04={"Benko","Programozasi feladatok es algoritmusok",1997,3850,269};
struct book b05={"Benko","Programozasi feladatok es algoritmusok",1997,3850,0};
if( (!strcmp(b01.szerzo,szerzokeres)) || (!strcmp(b02.szerzo,szerzokeres)) ||
(!strcmp(b03.szerzo,szerzokeres)) || (!strcmp(b04.szerzo,szerzokeres)) ||
(!strcmp(b05.szerzo,szerzokeres))) printf("Van ilyen\n");
else printf("NINCS ilyen\n");
return 0;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 19
STRUKTÚRATÖMB
• Struktúrákból álló tömb

struct penztarosok
{
int penztaros_szama ;
char penztaros_neve [ 51 ];
int szev;
int szho;
int sznap;
};
struct penztarosok penztaros [ 100 ] ;
© Valkai Zoltán 2020 ÓE/KVK/MAI 20
TÖMBSTRUKTÚRA
• Olyan struktúra, amely tömböket tartalmaz
• A tömbök csak fix méretűek lehetnek
• A tömbök mérete nem változtatható
struct hallgatok
{
char vnev [ 21 ] ;
char knev [ 31 ] ;
char nkod [ 7 ] ;
char telefon [ 15 ] ;
int szevhonap[ 3 ] ;
};
© Valkai Zoltán 2020 ÓE/KVK/MAI 21
UNION
• Definiálni és deklarálni kell, mint a struktúrát

• Leírás, mint a struktúránál

• Ugyanazt a memóriaterületet használja

• Méretét a legnagyobb értékű mező határozza meg

• Csak egy elem tárolható benne aktuálisan

• A régi felülíródik, ha új elemet írunk bele


© Valkai Zoltán 2020 ÓE/KVK/MAI 22
UNION
union allatkert
{
char allatnev [ 21 ] ;
char eledel [ 11 ] ;
int mennyiseg;
};
int main()
{
union allatkert allat;
printf("Az union merete: %i\n",sizeof(allat));
strcpy(allat.allatnev , "tigris");
printf("allatnev betoltve\n");
printf("eledel: %s\n",allat.eledel);
printf("allatnev: %s\n",allat.allatnev);
printf("eledel=>hus\n");
strcpy(allat.eledel , "hus");
printf("allatnev: %s\n",allat.allatnev);
printf("eledel: %s\n",allat.eledel);
return 0;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 23
STRUKTÚRA ÉS FÜGGVÉNY

• Függvény deklarációban be- kimenő


paraméterek típusa

• Struktúra típus esetén struktúra típusú

• A struktúra általunk létrehozott típus

• Csak cím szerint adható át

© Valkai Zoltán 2020 ÓE/KVK/MAI 24


STRUKTÚRA ÉS FÜGGVÉNY
• Függvény csak egy értéket adhat vissza

• Ez azonban lehet akár cím is

• A cím mutathat egy struktúrára

• Struktúracím visszaadásával megoldható több,


különböző típusú paraméter visszaadása

• Tömb címének visszaadásával csak azonos


típusú paraméterek adhatók vissza
© Valkai Zoltán 2020 ÓE/KVK/MAI 25
STRUKTÚRA ÉS FÜGGVÉNY
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 10

struct book
{
char szerzo [ 20 ] ; char cim [ 40 ] ;
int ev ; int ar ; int kikolcsonzo ;//-1: konyvtarban
};

int ktarkeres1(struct book *sp);


int ktarkeres2(struct book *sp, char *szp);
© Valkai Zoltán 2020 ÓE/KVK/MAI 26
STRUKTÚRA ÉS FÜGGVÉNY
int main()
{
char szerzokeres[20] = "Wirth";
struct book b[10]={
{"Kernighan","A C programozasi nyelv",1982,254,-1},
{"Kernighan","A C programozasi nyelv",1982,254,137},
{"Wirth","Algoritmusok+adatstrukturak",1974,74,-1},
{"Benko","Programozasi feladatok es algoritmusok",1997,3850,46},
{"Benko","Programozasi feladatok es algoritmusok",1997,3850,502}};

printf("A kikolcsonozheto konyvek darabszama: %i\n",ktarkeres1(b));

printf("A keresett szerzo konyveinek darabszama:


%i\n",ktarkeres2(b,szerzokeres));
return 0;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 27
STRUKTÚRA ÉS FÜGGVÉNY
int ktarkeres1 (struct book *sp )
{
int db = 0 , i ;
for( i = 0 ; i < N ; i++ )
{
if( ( *sp ).kikolcsonzo < 0 )
db++ ;
sp++ ;
}
return db ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 28
STRUKTÚRA ÉS FÜGGVÉNY
int ktarkeres2 ( struct book *sp , char *szp )
{
int i , db = 0 ;
for ( i = 0 ; i < N ; i++)
{
if ( ! (strcmp((*sp).szerzo , szp) ) )
db++ ;
sp++ ;
}
return db;
}

© Valkai Zoltán 2020 ÓE/KVK/MAI 29


KÖSZÖNÖM A FIGYELMET!

© Valkai Zoltán 2020 ÓE/KVK/MAI 30


PROGRAMOZÁS II.
8. Előadás

FÁJL-KEZELÉS
ALACSONY SZINTEN

© Valkai Zoltán 2020 ÓE/KVK/MAI 1


FÁJL ( FILE )
• Def.:

• Adatok összessége
• Fájlnévvel azonosított egy egységben tárolt
adatok halmaza
• Fájlnévvel azonosított adathalmaz
• Kapcsolódó adatok halmaza, amely a felhasználó
számára egyetlen, összefüggő információblokként
jelenik meg, amely a háttértáron található és
fáljnévvel azonosítható
© Valkai Zoltán 2020 ÓE/KVK/MAI 2
FÁJL ( FILE )
• Típusai
• Program ( futtatható állomány )
• Adat
• Szöveg
• Kép
• Hang
• Video
• Metaadat

© Valkai Zoltán 2020 ÓE/KVK/MAI 3


MIÉRT KELL?
• Programban csak azok az adatok használhatók,
amelyeket a program futásakor adunk meg

• Program változtatás esetén újra meg kell adni az


összes adatot

• A program kimeneti adatai csak a képernyőn


jelennek meg, nem tárolhatók

• Az egyes adathalmazokkal történő futtatás


eredményei nem hasonlíthatók össze
© Valkai Zoltán 2020 ÓE/KVK/MAI 4
UNIX
• Rendszerhívások
• Minden fájl
• Egy dimenziós bájttömb
• File Structure Related Calls
• Creating a Channel
• creat(), open(), close()
• Input/Output
• read(), write()
• Random Access
• lseek()
© Valkai Zoltán 2020 ÓE/KVK/MAI 5
UNIX

© Valkai Zoltán 2020 ÓE/KVK/MAI 6


MŰKÖDÉS
• pozíció
0 1 2 3 4 5 6 7 8 9 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2

• Írás / olvasás a fájl poziciót automatikusan növeli

© Valkai Zoltán 2020 ÓE/KVK/MAI 7


ÁLLOMÁNYOK

• Függvények: <stdio.h >

• Konstansok: <fcntl.h>

• Konstansok: <sys/stat.h>

© Valkai Zoltán 2020 ÓE/KVK/MAI 8


LÉTEZIK-E?
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>

#define fnev1 "test1.bin"


//#define fnev1 "main.c"
int main ( )
{
int fh1 , fc ;
fh1 = open ( fnev1 , O_RDONLY ) ;
fc = close ( fh1 ) ;
printf ( "fh: %i\n" , fh1 ) ;
printf ( "fc: %i\n" , fc ) ;
return 0;
}
Ha nincs ilyen => -1 -1 sikertelen
Ha van => 3 0 0: stdin 1: stdout stderr: 2
© Valkai Zoltán 2020 ÓE/KVK/MAI 9
#include < fcntl.h >
#define _O_RDONLY 0
#define _O_WRONLY 1
#define _O_RDWR 2

#define _O_CREAT 0x0100 /* Create the file if it


does not exist. */
#define _O_TRUNC 0x0200/* Truncate the file if it
does exist. */
#define _O_EXCL 0x0400 /* Open only if the file
does not exist. */
© Valkai Zoltán 2020 ÓE/KVK/MAI 10
fcntl.h
#define _O_TEXT 0x4000 /* CR-LF in file becomes
LF in memory. */
#define _O_BINARY 0x8000 /* Input and output is
not translated. */
#define _O_APPEND 0x0008 /* Writes will add to
the end of the file. */
BINARY → pontosan az kerül bele, amit beleírunk
Méret számítható
TEXT → 0A : 0D 0A → 2B; 266 : 0D 0A 01 00 00 → 5B
2570 : 0D 0A 0D 0A 00 00 → 6B
657930: 0D 0A 0D 0A 0D 0A 00 → 7B
© Valkai Zoltán 2020 ÓE/KVK/MAI 11
LÉTREHOZÁS1
#define fnev2 "test2.bin"
int main ( )
{
int fh2 , fc ;
fh2 = open ( fnev2 , O_CREAT | O_WRONLY ) ;
fc = close ( fh2 ) ;
printf ( "fh: %i\n" , fh2 ) ;
printf ( "fc: %i\n" , fc ) ;
return 0;
}
• Ha nem létezik => 3 0
• Ha létezik => -1 -1 attr: RA
© Valkai Zoltán 2020 ÓE/KVK/MAI 12
LÉTREHOZÁS2
#define fnev3 "test3.bin"
int main ( )
{
int fh3 , fc ;
fh3 = open ( fnev3 , O_CREAT | O_RDWR ,
S_IREAD | S_IWRITE ) ;
fc = close ( fh3 ) ;
printf ( "fh: %i\n" , fh3 ) ;
printf ( "fc: %i\n" , fc ) ;
return 0;
}
// Ha nem létezik => 3 0
// Ha létezik => 3 0
© Valkai Zoltán 2020 ÓE/KVK/MAI
attr: A 13
#include < sys/stat.h >
#define _S_IEXEC 0x0040

#define _S_IWRITE 0x0080

#define _S_IREAD 0x0100

© Valkai Zoltán 2020 ÓE/KVK/MAI 14


ÍRÁS1
#define fnev4 "test4.bin"
int main ( )
{
int fh4 , fc , y ;
fh4 = open ( fnev4 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
y = write ( fh4 , &fh4 , sizeof ( fh4 ) ) ;
fc = close ( fh4 ) ;
printf ( "fh: %i\n" , fh4 ) ;
printf ( "y: %i\n" , y ) ;
printf ( "fc: %i\n" , fc ) ;
return 0;
}
• 3 4 0

© Valkai Zoltán 2020 ÓE/KVK/MAI 15


ÍRÁS1 - ELLENŐRZÉS
Total Commander

test4.bin- re ráálni
F3

A windows mindent szövegként szeretne megmutatni, de a nézőben


Beállítások – Hexa

© Valkai Zoltán 2020 ÓE/KVK/MAI 16


ÍRÁS2
#define fnev5 "test5.t"
int main ( )
{
int fh5 , fc , x = 10 , y ;
fh5 = open ( fnev5 , O_CREAT | O_RDWR | O_TEXT ,
S_IREAD | S_IWRITE ) ;
y = write ( fh5 , &x , sizeof ( x ) ) ;
fc = close ( fh5 ) ;
printf ( "fh: %i\n" , fh5 ) ;
printf ( "y: %i\n" , y ) ;
printf ( "fc: %i\n" , fc ) ;
return 0;
}
• 3 4 0
• De a test.t fájl mérete 5B!!! X=266 → 6B; 657930 → 7B
© Valkai Zoltán 2020 ÓE/KVK/MAI 17
FELÜLÍRÁS
#define fnev6 "test6.bin"
int main ( )
{
int fh6 , fc , x1 = 266, x2 = 10 , x3 = 9 , y ;
fh6 = open ( fnev6 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
y = write ( fh6 , &x1 , sizeof ( x1 ) ) ;
y = write ( fh6 , &x2 , sizeof ( x2 ) ) ; fc = close ( fh6 ) ;
fh6 = open ( fnev6 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
y = write ( fh6 , &x3 , sizeof ( x3 ) ) ;
fc = close ( fh6 ) ;
return 0;
}
09 00 00 00 0A 00 00 00
© Valkai Zoltán 2020 ÓE/KVK/MAI 18
HOZZÁFŰZÉS
#define fnev7 "test7.bin"
int main ( )
{
int fh7 , fc , x1 = 266, x2 = 10 , x3 = 9 , y ;
fh7 = open ( fnev7 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
y = write ( fh7 , &x1 , sizeof ( x1 ) ) ;
y = write ( fh7 , &x2 , sizeof ( x2 ) ) ; fc = close ( fh7 ) ;
fh7 = open ( fnev7 , O_APPEND | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
y = write ( fh7 , &x3 , sizeof ( x3 ) ) ;
fc = close ( fh7 ) ;
return 0;
}
0A 01 00 00 0A 00 00 00 09 00 00 00 újabb futtatáskor mi történik?
© Valkai Zoltán 2020 ÓE/KVK/MAI 19
CSONKOLÁS
#define fnev8 "test8.bin"
int main ( )
{
int fh8 , fc , x1 = 266, x2 = 10 , x3 = 0x2a , y ;
fh8 = open ( fnev8 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
y = write ( fh8 , &x1 , sizeof ( x1 ) ) ;
y = write ( fh8 , &x2 , sizeof ( x2 ) ) ; fc = close ( fh8 ) ;
fh8 = open ( fnev8 , O_TRUNC | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
y = write ( fh8 , &x3 , sizeof ( x3 ) ) ; fc = close ( fh8 ) ;
return 0;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 20
ÍRÁS + OLVASÁS
#define fnev9 "test9.bin"
int main ( )
{
int fh9 , fc , x1 = 13, x2 = 31 , x3 = 0 , y ;
fh9 = open ( fnev9 , O_CREAT | O_RDWR | O_BINARY , S_IREAD | S_IWRITE ) ;
y = write ( fh9 , &x1 , sizeof ( x1 ) ) ;
y = write ( fh9 , &x2 , sizeof ( x2 ) ) ; fc = close ( fh9 ) ;
fh9 = open ( fnev9 , O_RDWR | O_BINARY , S_IREAD | S_IWRITE ) ;
y = read ( fh9 , &x3 , sizeof ( x3 ) ) ; printf ( "y: %i\n" , y ) ;
y = read ( fh9 , &x3 , sizeof ( x3 ) ) ; printf ( "y: %i\n" , y ) ;
y = read ( fh9 , &x3 , sizeof ( x3 ) ) ; printf ( "y: %i\n" , y ) ;
fc = close ( fh9 ) ;
return 0;
}
4 4 0
© Valkai Zoltán 2020 ÓE/KVK/MAI 21
POZICIONÁLÁS
#define fnev10 "test10.bin"
int main ( )
{
int fh10 , fc , x1 = 13, x2 = 31 , x3 = 11 , y ;
fh10 = open ( fnev10 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
y = write ( fh10 , &x1 , sizeof ( x1 ) ) ;
y = write ( fh10 , &x2 , sizeof ( x2 ) ) ;
y = write ( fh10 , &x3 , sizeof ( x3 ) ) ; fc = close ( fh10 ) ;
fh10 = open ( fnev10 , O_RDWR | O_BINARY , S_IREAD | S_IWRITE )
;
lseek ( fh10 , 8 , SEEK_SET ) ; y = read ( fh10 , &x3 , sizeof ( x3 ) ) ;
printf ( "x3: %i\n" , x3 ) ; fc = close ( fh10 ) ;
return 0;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 22
FÁJL ÍRÁS ÉS OLVASÁS CIKLUSSAL
#define fnev11 "test11.bin"
int main ( )
{
int fh11 , fc , x = 1 , y = 0 , i;
fh11 = open ( fnev11 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
for ( i=1 ; i <= 100 ; i++ , x++ ) write ( fh11 , &x , sizeof ( x ) ) ;
lseek ( fh11 , 0 , SEEK_SET ) ;
while ( read ( fh11 , &x , sizeof ( x ) ) )
y=y+x;
printf ( "y: %i\n" , y ) ; close ( fh11 ) ;
return 0;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 23
TÖMB ÍRÁSA / OLVASÁSA
#define fnev12 "test12.bin"
int main ( )
{
int fh12 , x [ ] = { 2 , 4 , 6 , 8 } , y = 0 , w ;
fh12 = open ( fnev12 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
write ( fh12 , x , sizeof ( x ) ) ;
lseek ( fh12 , 0 , SEEK_SET ) ;
while ( read ( fh12 , &w , sizeof ( w ) ) )
y=y+w;
printf ( "y: %i\n" , y ) ; close ( fh12 ) ;
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 24
KARAKTERTÖMB1
#define fnev13 "test13.bin"
int main ( )
{
int fh13 ; char k , kt [ ] = "Ekszer a\n MUSZER!" ;
fh13 = open ( fnev13 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
write ( fh13 , kt , sizeof ( kt ) ) ;
lseek ( fh13 , 0 , SEEK_SET ) ;
while( read ( fh13 , &k , sizeof ( k ) ) )
printf ( "%c" , k ) ;
printf ( "\n" ) ; close ( fh13 ) ;
return 0;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 25
KARAKTERTÖMB2
#define fnev14 "test14.bin"
int main ( )
{
int fh14 , db = 0 ; char k , kt [ ] = "Ekszer a\n MUSZER!" ;
fh14 = open ( fnev14 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
write ( fh14 , kt , sizeof ( kt ) ) ;
lseek ( fh14 , 0 , SEEK_SET ) ;
while( read ( fh14 , &k , sizeof ( k ) ) ) if ( k == 'E') db++;
printf ( "Az 'E' betuk darabszama: %i\n " , db) ;
close ( fh14 ) ;
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 26
STRUKTÚRA
struct telkonyv {char nev [ 15 ] ; int tszam;} ;
int main ( )
{
int fh15 , db = 0 ; struct telkonyv tk[ ] =
{"Fekete",16665110,"Barna",309753113,"Pink",207070155}, tktmp;
fh15 = open ( fnev15 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
write ( fh15 , tk , sizeof ( tk ) ) ; lseek ( fh15 , 0 , SEEK_SET ) ;
while ( read ( fh15 , &tktmp , sizeof ( tktmp ) ) )
printf ( "%20s %8i\n" , tktmp.nev, tktmp.tszam) ;
close ( fh15 ) ;
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 27
FÁJL MÉRETE
#define fnev16 "test16.bin"
int main ( )
{
short int fh16 , i , m = 0 ;
fh16 = open ( fnev16 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
for ( i = 3 ; i < 30 ; i ++) write ( fh16 , &i , sizeof ( i ) ) ;
m = tell ( fh16 ) ;
printf ( "A file merete: %4i B\n" , m) ;
close ( fh16 ) ;
fh16 = open ( fnev16 , O_RDWR , S_IREAD | S_IWRITE ) ;
lseek ( fh16 , 0 , SEEK_END ) ; m = tell ( fh16 ) ;
printf ( "A file merete: %4i B\n" , m) ; close ( fh16 ) ;
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 28
ADAT FELÜLÍRÁSA
#define fnev17 "test17.bin"
int main ( )
{
int fh17 , i , m = 0 ;
char kt [ ] = "Volt egyszer egy vadnyugat -:)" , k , cs = '*' ;
fh17 = open ( fnev17 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
write ( fh17 , kt , sizeof ( kt ) ) ; lseek (fh17 , 0 , SEEK_SET ) ;
while( read( fh17, &k, sizeof (k) ) ) if ( k == ' ' )
{
lseek ( fh17 , -sizeof ( k ) , SEEK_CUR ) ;
write ( fh17 , &cs , sizeof ( cs ) ) ; m ++ ;
}
close ( fh17 ) ; printf ( "A cserelt darabszam: %i\n" , m ) ;
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 29
MÉRET MEGVÁLTOZTATÁSA
#define fnev18 "test18.bin"
int main ( )
{
int fh18 , i , m = 0 ;
char kt [ ] = "Volt egyszer egy vadnyugat -:)" , k , cs = '*' ;
fh18 = open ( fnev18 , O_CREAT | O_RDWR | O_BINARY ,
S_IREAD | S_IWRITE ) ;
write ( fh18 , kt , sizeof ( kt ) ) ;
lseek ( fh18 , 0 , SEEK_SET ) ;
chsize ( fh18, 16 ) ;
close ( fh18 ) ;
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 30
LÉTREHOZÁS
#define fnev19 "test19.bin"
int main ( )
{
int fh19 , i , m = 0 ;
short int it [ ] = {0, 1, 4, 9, 16, 25, 36, 49} ;
fh19 = creat ( fnev19 , S_IREAD | S_IWRITE ) ; //
write ( fh19 , it , sizeof ( it ) ) ;
m = tell ( fh19 ) ;
lseek ( fh19 , 0 , SEEK_SET ) ;
close ( fh19 ) ;
printf ( "A file merete: %i\n" , m );
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 31
MŰVELET TÖBB FÁJL-lal
#define fnev20 "test20.bin"
#define fnev21 "test21.bin"
int main ( )
{
int fh21 , fh20 , y = 0 ; char kt[8]={0};
short int it [ ] = {0,1,1,2,3,5,8,13,21,34,55} , itt;
fh20 = open ( fnev20 , O_CREAT | O_RDWR | O_BINARY | O_TRUNC
, S_IREAD | S_IWRITE ) ;
write ( fh20 , it , sizeof ( it ) ) ;
lseek ( fh20 , 0 , SEEK_SET ) ;
fh21 = creat ( fnev21 , S_IREAD | S_IWRITE ) ;
while (read ( fh20, &itt , sizeof (itt) ) )
{
write ( fh21, itoa (itt , kt , 10 ) , sizeof ( kt ) ) ;
}
close ( fh20 ) ;
close ( fh21 ) ;
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 32
KÖSZÖNÖM A FIGYELMET!

© Valkai Zoltán 2020 ÓE/KVK/MAI 33


PROGRAMOZÁS II.
9. Előadás

FÁJL-KEZELÉS
MAGAS SZINTEN

© Valkai Zoltán 2020 ÓE/KVK/MAI 1


TULAJDONSÁGAI
• Bármilyen művelet előtt → megnyitás
• Működés (írás/olvasás) szekvenciális
• Fájl írás / olvasás nem közvetlenül történik
• Puffer
• Operatív memóriában
• Puffernek mérete van (véges)
• Amikor megtelik, akkor kerül sor fizikai írásra
• Blokkot ír
• Olvasásnál a végén újratölti a puffert
• Írás elvileg gyorsabb (nem fizikai írás, aszinkron)
• Olvasás lassabb (ritka adatra)
© Valkai Zoltán 2020 ÓE/KVK/MAI 2
FILE struktúra
• Minden fájl csak addig legyen megnyiva, amíg
feltétkenül kell!!!
typedef struct _iobuf
{
char* _ptr; //
int _cnt; // maradék
char* _base; // ua. _ptr
int _flag; // attr.
int _file; // file handle ←――――――
int _charbuf; // 0
int _bufsiz; // 512 B
char* _tmpfname; // null
} FILE; © Valkai Zoltán 2020 ÓE/KVK/MAI 3
KONSTANSOK
<stdio.h>
STDIN 0
STDOUT 1
STDERR 2
EOF -1
filename_MAX 260
fopen_MAX 20 // sok megnyitott állomány
SEEK_SET 0
SEEK_CUR 1
SEEK_END 2

NULL null pointer


© Valkai Zoltán 2020 ÓE/KVK/MAI 4
MEGNYITÁS és a „mode” string
Hivatkozás: FILE típusú pointer
FILE *fp ;
Megnyitás:
fp = fopen ( file_azonosito , " megnyitási mód " ) ;
r csak olvasás, ha nem létezik → nem hozza létre
r+ írás – olvasás, ha létezik → változatlan marad , nem hozza létre
w írás, ha nem létezik létrehozza, ha létezik → csonkolja
w+ írás – olvasás – létrehozás, ha létezik → csonkolja
a hozzáfűzés, ha nem létezik → létrehozza
a+ hozzáfűzés és olvasás, ha nem létezik → létrehozza

Alapértelmetett: " t"


" b" bináris mód, mint alacsony szinten

© Valkai Zoltán 2020 ÓE/KVK/MAI 5


FÜGGVÉNYEK
Lezárás:
int = fclose( fp ) ; // a puffert is üríti

Puffer ürítés:
int = fflush ( fp ) ;

Pozicionálás: // a puffert is üríti


int = fseek ( fp ) ;

int = fileno ( fp ) ;

© Valkai Zoltán 2020 ÓE/KVK/MAI 6


LÉTEZIK-E?
#define FNEV1 "hifile1.dat"
int main ( )
{
FILE *fp1 ;
int x ;
fp1 = fopen ( FNEV1 , "r " ) ;
if ( fp1 == NULL ) printf ( "A fajl NEM letezik\n " ) ;
else
{
printf ( "A fajl letezik!\n " ) ;
x = fclose ( fp1 ) ;
printf ( "x: %i\n " , x ) ; // x = ?
}
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 7
LÉTREHOZÁS
#define FNEV2 "hifile2.dat"
int main( )
{
FILE *fp2 ; int x ;
fp2 = fopen ( FNEV2 , "w" ) ;
x = fclose ( fp2 ) ;
if ( fp2 == NULL )
printf ( "A fajl letrehozas sikertelen\n " ) ;
else
printf ( "A fajl letrehozas sikerult!\n " ) ;
printf ( "x: %i\n " , x ) ; // x = ?
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 8
ÍRÁS (bináris)
#define FNEV3 "hifile3.dat"
int main ( )
{
FILE *fp3 ;
int x = 100 ;
fp3 = fopen ( FNEV3 , "wb" ) ; // ha x = 10 ?
if ( fp3 == NULL )
printf ("A fajlt nem sikerult letrehozni\n " ) ;
else
{
x = fwrite ( &x, sizeof ( x ) , 1 , fp3 ) ;
fclose ( fp3 ) ;
printf ( "x: %i\n " , x ) ; // x = ?
}
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 9
ÍRÁS (bináris)
#define FNEV4 "hifile4.dat"
int main ( )
{
FILE *fp4 ;
int x = 100 ;
fp4 = fopen ( FNEV4 , "wb" ) ;
if ( fp4 == NULL )
{
printf ( "A fajlt nem sikerult letrehozni\n " ) ;
}
else
{
x = fwrite ( &x , sizeof ( x ) , 5 , fp4 ) ; // mit fog kiírni ?
fclose ( fp4 ) ;
printf ( "x: %i\n " , x ) ; // x = ?
}
return 0 ;
© Valkai Zoltán 2020 ÓE/KVK/MAI 10
}
ÍRÁS (ciklussal)
#define FNEV5 "hifile5.dat"
int main()
{
FILE *fp5 ;
short int x = 250 , i , y ;
fp5 = fopen ( FNEV5 , "wb" ) ; // mi lesz b nélkül ?
if(fp5 == NULL)
printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
for ( i = 0 ; i < 20 ; i ++ , x ++ )
fwrite ( &x , sizeof ( x ) , 1 , fp5 ) ; // mi lesz benne ?
y = fclose ( fp5 ) ;
if ( y )
printf ( "File lezaras sikertelen\n" ) ;
}
return 0 ;
© Valkai Zoltán 2020 ÓE/KVK/MAI 11
}
ÍRÁS (tömb)
#define FNEV6 "hifile6.dat"
int main()
{
FILE *fp6 ; int w , y ; short int it [ ] = { 2 , 4 , 6 , 8 , 10 , 12 } ;
fp6 = fopen ( FNEV6 , "w+b" ) ;
if ( fp6 == NULL ) printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
w = fwrite ( it , sizeof ( it ) , 1 , fp6 ) ;
// w = fwrite ( it , 1 , sizeof ( it ) , fp6 ) ;
printf ( "w: %i" , w ) ; // w = ?
y = fclose ( fp6 ) ;
if ( y )
printf ( "File lezaras sikertelen\n" ) ;
} return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 12
ÍRÁS (karaktertömb)
#define FNEV7 "hifile7.dat"
int main()
{
FILE *fp7 ; int w , y ;
char kt [ ] = "Ekszer a MUSZER!" ;
fp7 = fopen ( FNEV7 , "w" ) ;
if(fp7 == NULL)
printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
w = fwrite ( kt , sizeof(kt), 1 , fp7 ) ;
w = ftell ( fp7 ) ;
y = fclose ( fp7 ) ;
if ( y )
printf ( "File lezaras sikertelen\n" ) ;
printf ( "w : %i\n" , w ) ; // w = ?
}
return 0 ;
© Valkai Zoltán 2020 ÓE/KVK/MAI 13
}
ÍRÁS (struktúra)
#define FNEV8 "hifile8.dat"
struct aruk { int cikkszam ; char megnevezes[21] ; int ar ; } ;
int main()
{
FILE *fp8 ; int w , y ;
struct aruk aru [ 5 ] = {16460, "GIGABYTE B450M DS3H" , 24190,
16840 , "ASUS PRIME B450M-A" , 23590} ;
fp8 = fopen ( FNEV8 , "wb" ) ;
if(fp8 == NULL)
printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else {
w = fwrite ( &aru[0].cikkszam , sizeof(aru[0].cikkszam), 1 , fp8 ) ;
w = fwrite ( &aru [ 0 ] .megnevezes , sizeof(aru[0].megnevezes), 1 , fp8 ) ;
w = fwrite ( &aru [ 0 ] .ar , sizeof(aru[0].ar), 1 , fp8 ) ;
w = fwrite ( &aru [ 1 ] , sizeof(aru[1]), 1 , fp8 ) ;
w = ftell ( fp8 ) ;
y = fclose ( fp8 ) ;
if ( y )
printf ( "File lezaras sikertelen\n" ) ;
printf ( "w : %i\n" , w ) ; } // w = ?
return 0 ;
© Valkai Zoltán 2020 ÓE/KVK/MAI 14
}
OLVASÁS
int fread ( *ptr , adat mennyiség , blokkok száma , *FILE ) ;
megnyitási mód
*ptr címre olvas, mint a scanf ( ) függvény
adat mennyiség B-ban
blokkok száma hány darabot az (adat mennyiségből)
*FILE struktúra pointer

visszatérési érték a beolvasott blokkok darabszáma


while ( 1 ) { y = fread ( , , , ) ; if ( ! y ) break ; }
while ( fread ( , , , ) ) { }

© Valkai Zoltán 2020 ÓE/KVK/MAI 15


OLVASÁS
#define FNEV9 "hifile9.dat " // " \\data\\hifile9.dat"
int main()
{
FILE *fp9 ; int x = 0 , y , i ;
fp9 = fopen ( FNEV9 , "w+b" ) ; // későbbi olvasás miatt 5. dia
if ( fp9 == NULL )
printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
for ( i = 100 , y = 0 ; i < 130 ; i ++ )
y = y + fwrite ( &i , sizeof ( i ) , 1 , fp9 ) ;
printf ( "osszesen irt blokk: %i\n" , y ) ; // y = ?
y = fseek ( fp9 , 0 , SEEK_SET) ;
while ( fread ( &x , sizeof ( x ) , 1 , fp9 ) )
printf ( "%3i\n" , x ) ; // mit ír ki?
fclose ( fp9 ) ;
}
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 16
FELÜLÍRÁS páros→páratlan
#define FNEV10 "hifile10.dat"
int main()
{
FILE *fp10 ; int x = 0 , y , i ;
fp10 = fopen ( FNEV10 , "w+b" ) ; // későbbi olvasás miatt
if ( fp10 == NULL ) printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
for ( i = 230 , y = 0 ; i < 256 ; i ++ ) y = y + fwrite ( &i , sizeof ( i ) , 1 , fp10 ) ;
y = fseek ( fp10 , 0 , SEEK_SET ) ;
while ( fread ( &x , sizeof ( x ) , 1 , fp10 ) )
{
if ( ! ( x % 2 ) ) //
{
fseek ( fp10 , - sizeof ( x ) , SEEK_CUR ) ; x ++ ;
fwrite ( &x , sizeof ( x ) , 1 , fp10 ) ; fflush ( fp10 ) ; //!!!
}
}
fclose ( fp10 ) ;
} return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 17
HOZZÁFŰZÉS
#define FNEV11 "hifile11.dat"
int main ( )
{
FILE *fp11 ; int x = 0 , y , i = 20 ;
fp11 = fopen ( FNEV11 , "rb" ) ;
if ( fp11 == NULL )
{
fp11 = fopen ( FNEV11 , "wb" ) ; fwrite ( &i , sizeof ( i ) , 1 , fp11 ) ;
fclose ( fp11 ) ; printf ( "A file nem letezett, letrehoztam\n" ) ;
}
else
{
while ( fread ( &x , sizeof ( x ) , 1 , fp11 ) ) ;
printf( "Utolso olvasott adat: %i\n" , x ) ;
fflush ( fp11 ) ; fclose ( fp11 ) ;
fp11 = fopen ( FNEV11 , "ab " ) ;
x=x+2; fwrite ( &x , sizeof ( x ) , 1 , fp11 ) ; printf( "Hozzafuzott adat: %i\n" , x ) ;
fflush ( fp11 ) ; fclose ( fp11 ) ; // x = ?
}
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 18
CSONKOLÁS - 1
#define FNEV12 "hifile12.dat"
#define FNEV13 "hifile13.dat"
int main()
{
FILE *fp12 , *fp13 ; int x = 0 , y , i ;
fp12 = fopen ( FNEV12 , "rb" ) ;
if ( fp12 == NULL )
{
fp12 = fopen ( FNEV12 , "wb" ) ; for ( i = 270 ; i < 300 ; i++ ) fwrite ( &i , sizeof ( i ) , 1 , fp12 ) ;
fclose ( fp12 ) ; printf ( "A file nem letezett, letrehoztam\n" ) ;
}
else
{
fp13 = fopen ( FNEV13 , "wb" ) ; i = 10 ;
while ( i )
{
fread ( &x , sizeof ( x ) , 1 , fp12 ) ; fwrite ( &x , sizeof ( x ) , 1 , fp13 ) ;
i -- ;
}
fclose ( fp12 ) ; fclose ( fp13 ) ; // mekkora méretűek ?
}
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 19
CSONKOLÁS - 2
#define FNEV14 "hifile14.dat"
int main()
{
FILE *fp14 ; int x = 0 , y , i ;
fp14 = fopen ( FNEV14 , "r+b" ) ;
if ( fp14 == NULL )
{
fp14 = fopen ( FNEV14 , "wb" ) ;
for ( i = 4090 ; i < 4120 ; i++ ) fwrite ( &i , sizeof ( i ) , 1 , fp14 ) ;
fclose ( fp14 ) ;
printf ( "A file nem letezett, letrehoztam\n" ) ;
}
else
{
x = 40 ; i = fileno ( fp14 ) ; // *FILE -> int
chsize ( i, 40 ) ; // alacsony szint !
fclose ( fp14 ) ;
}
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 20
OLVASÁS – ÍRÁS feladat
• Létrehozunk 3 bináris állományt
• Kettőbe egész típusú adatokat írunk
• Függvénnyel
• …
• Folyamatosan olvassuk a két adatállományt
• A beolvasott adatok összege lesz az az eredmény,
ami a 3. állományba kerül
• Ha az egyik állományból az adatok elfogytak,
akkor csak a másikban lévő adat legyen az
eredmény állományban

© Valkai Zoltán 2020 ÓE/KVK/MAI 21


OLVASÁS - ÍRÁS
#define FNEV15 "hifile15.dat"
#define FNEV16 "hifile16.dat"
#define FNEV17 "hifile17.dat"
FILE *fv15 ( char *fname , int kezd , int delta , int db ) ;
int main()
{
FILE *fp15, *fp16 , *fp17 ; int i , w15 , w16 , x15 , x16 ;
fp15 = fv15 ( FNEV15 , 10 , 1 , 40 ) ; fp16 = fv15 ( FNEV16 , 100 , 1 , 20 ) ;
fp17 = fv15 ( FNEV17 , 0 , 0 , 0 ) ; if ( fp15 == NULL || fp16 == NULL || fp17 == NULL )
printf ( "A fajlokat nem sikerult letrehozni\n" ) ;
else
{
fseek(fp15 , 0 , SEEK_SET) ; fseek(fp16 , 0 , SEEK_SET) ;
while ( 1 )
{
w15 = fread ( &x15 , sizeof ( x15 ) , 1 , fp15 ) ;
w16 = fread ( &x16 , sizeof ( x16 ) , 1 , fp16 ) ;
if ( ! ( w15 || w16 ) ) break ; i = x15 + x16 ; fwrite ( &i , sizeof ( i ) , 1 , fp17) ;
}
fclose ( fp15 ) ; fclose ( fp16 ) ; fclose ( fp16 ) ;
} return 0 ; © Valkai Zoltán 2020 ÓE/KVK/MAI 22
}
OLVASÁS - ÍRÁS
FILE *fv15 ( char *fname , int kezd , int delta , int db )
{
FILE *fp15;
int n , i ;
fp15 = fopen ( fname , "w+b" ) ;
if ( fp15 == NULL ) return fp15 ;
for ( i = kezd , n = 0 ; n < db ; i = i + delta , n++ )
fwrite ( &i , sizeof ( i ) , 1 , fp15 ) ;
return fp15 ;
}

© Valkai Zoltán 2020 ÓE/KVK/MAI 23


POZICIONÁLÁS
#define FNEV18 "hifile18.dat"
int main()
{
FILE *fp18 ; short int it [ ] = {55 , 50 , 45 , 40 , 35 , 30 , 25 , 20 } ,
tmeret = sizeof ( it ) / sizeof ( it [ 0 ] ) , fmeret , tmp , x , poz = 0 ;
fp18 = fopen ( FNEV18 , "wb" ) ;
if ( fp18 == NULL ) printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else

© Valkai Zoltán 2020 ÓE/KVK/MAI 24


POZICIONÁLÁS
{
fwrite ( it , tmeret , sizeof ( it [ 0 ] ) , fp18 ) ; fclose ( fp18 ) ;
fp18 = fopen ( FNEV18 , "r+b" ) ; //olvasás-írás
tmeret = tmeret / 2 ; poz =0;
while ( tmeret )
{
fseek ( fp18 , poz , SEEK_SET ) ; fread ( &x , sizeof ( x ) , 1 , fp18 ) ;
fseek ( fp18 , -sizeof(x)-poz , SEEK_END ) ;
fread ( &tmp , sizeof ( tmp ) , 1 , fp18 ) ;
fseek ( fp18 , -sizeof(x)-poz , SEEK_END ) ;
fwrite ( &x , sizeof ( x ) , 1 , fp18) ;
fseek ( fp18 , poz , SEEK_SET ) ; fwrite ( &tmp , sizeof ( tmp ) , 1 , fp18) ;
tmeret -- ; poz = poz + sizeof ( x ) ;
}
fclose ( fp18 ) ;
}
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 25
KARAKTERTÖMB ÍRÁSA - OLVASÁSA
#define FNEV19 "hifile19.dat"
int main()
{
FILE *fp19 ; int it [ ] = {1814067781, 544502885, 1852600182, 543236193,
1634493798, 7627108} ; char k ;
int tmeret = sizeof ( it ) / sizeof ( it [ 0 ] ) ;
fp19 = fopen ( FNEV19 , "wb" ) ;
if ( fp19 == NULL ) printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
fwrite ( it , tmeret , sizeof ( it [ 0 ] ) , fp19 ) ; fclose ( fp19 ) ;
fp19 = fopen ( FNEV19 , "r+b" ) ; //olvasás-írás
while ( fread ( &k , sizeof ( k ) , 1 , fp19 ) )
printf("%c",k);
fclose ( fp19 ) ;
printf("\n\n");
}
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 26
KARAKTERTÖMB ÍRÁSA - OLVASÁSA
#define FNEV20 "hifile20.dat"
int main()
{
FILE *fp20 ; char it [ ] = "Obudai Egyetem\nKando\r Kalman\t
Villamosmernoki Kar\n\n1970 / 2000 / 2010\n" ,
tmeret = sizeof ( it ) / sizeof ( it [ 0 ] ) , x ;
fp20 = fopen ( FNEV20 , "w" ) ;
if ( fp20 == NULL ) printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
fwrite ( it , tmeret , sizeof ( it [ 0 ] ) , fp20 ) ; fclose ( fp20 ) ;
fp20 = fopen ( FNEV20 , "r" ) ; //olvasás-írás
while ( fread ( &x , sizeof ( x ) , 1 , fp20 ) )
printf ( "%c" , x ) ;
fclose ( fp20 ) ;
}
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 27
STRUKTÚRA ÍRÁSA - OLVASÁSA
#define FNEV21 "hifile21.dat"
struct hegycsucsok{ char hegycsucs_neve[17]; int magassag;};
int main()
{
FILE *fp21 ;
struct hegycsucsok csucsok[]={"Kekes",1014,"Szilvasi-ko",961,"Csovanyos",938} , csucs;
fp21 = fopen ( FNEV21 , "wb" ) ;
if ( fp21 == NULL ) printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
fwrite ( csucsok , sizeof(csucsok) , 1 , fp21 ) ; fclose ( fp21 ) ;
fp21 = fopen ( FNEV21 , "rb" ) ; // olvasás
while ( fread ( &csucs , sizeof ( csucs ) , 1 , fp21 ) )
{
printf ( "%s\n" , csucs.hegycsucs_neve ) ;
printf ( "%i\n" , csucs.magassag ) ;
}
fclose ( fp21 ) ;
}
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 28
FÁJL MÉRETE
#define FNEV22 "hifile22.dat"
int main()
{
FILE *fp22 ; int x , fmeret ;
fp22 = fopen ( FNEV22 , "wb" ) ;
if ( fp22 == NULL ) printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
for ( x = -150 ; x < 150 ; x = x + 3 ) fwrite ( &x , sizeof(x) , 1 , fp22 ) ;
fmeret = ftell ( fp22 ) ; fclose ( fp22 ) ;
printf ( "A file merete: %i B\n" , fmeret ) ; // mennyi ?
fp22 = fopen ( FNEV22 , "wb" ) ;
for ( x = 150 ; x < 300 ; x = x + 3 ) fwrite ( &x , sizeof(x) , 1 , fp22 ) ;
fmeret = ftell ( fp22 ) ; // mennyi ?
fclose ( fp22 ) ;
printf ( "A file merete: %i B\n" , fmeret ) ;
}
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 29
MÉRET MEGVÁLTOZTATÁSA
#define FNEV23 "hifile23.dat"
int main()
{
FILE *fp23 ; int x , fmeret ; fp23 = fopen ( FNEV23 , "wb" ) ;
if ( fp23 == NULL ) printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
for ( x = -120 ; x < 120 ; x = x + 3 )
fwrite ( &x , sizeof(x) , 1 , fp23 ) ;
fmeret = ftell ( fp23 ) ; fclose ( fp23 ) ; // mennyi ?
printf ( "A file merete: %i B\n" , fmeret ) ;
fp23 = fopen ( FNEV23 , "wb" ) ;
for ( x = 200 ; x < 400 ; x = x + 2 )
fwrite ( &x , sizeof(x) , 1 , fp23 ) ;
fmeret = ftell ( fp23 ) ; // mennyi ?
fclose ( fp23 ) ;
printf ( "A file merete: %i B\n" , fmeret ) ;
} return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 30
TÖBB FÁJL HASZNÁLATA
Írjuk bináris fájlba [ -60 , 60 ) 3-asával növekvő egészeket
Olvassuk ki az adatokat, az egyik fájlba a 4-gyel nem oszthatók,
A másikba a 4-gyel oszthatók kerüljenek

#define FNEV24 "hifile24.dat"


#define FNEV25 "hifile25.dat"
#define FNEV26 "hifile26.dat"
int main()
{
FILE *fp24 , *fp25 , *fp26 ; int x , fmeret ;
fp24 = fopen ( FNEV24 , "wb" ) ;
if ( fp24 == NULL ) printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
© Valkai Zoltán 2020 ÓE/KVK/MAI 31
TÖBB FÁJL HASZNÁLATA
{
for ( x = -60 ; x < 60 ; x = x + 3 )
fwrite ( &x , sizeof(x) , 1 , fp24 ) ;
fclose ( fp24 ) ;
fp24 = fopen ( FNEV24 , "rb" ) ;
fp25 = fopen ( FNEV25 , "wb" ) ;
fp26 = fopen ( FNEV26 , "wb" ) ;
while ( fread ( &x, sizeof ( x ) , 1 , fp24 ) )
{
if ( x % 4 ) fwrite ( &x , sizeof(x) , 1 , fp25 ) ; //
else fwrite ( &x , sizeof(x) , 1 , fp26 ) ; //
}
fclose ( fp24 ) ; fclose ( fp25 ) ; fclose ( fp26 ) ;
}
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 32
FORMÁTUMOZOTT FÁJL MŰVELET
• Elvek
• C-ben minden fájl ( stdin , stdout , stderr )
• Jó
– Szövegként olvasható ( nem csak a profiknak )
– Ellenőrzés csak szövegként lehetséges

• Rossz
– Fájl mérete nem számítható
– Fájl formátumát ismernünk kell
– Fájl sérülése nehezebben detektálható
© Valkai Zoltán 2020 ÓE/KVK/MAI 33
FORMÁTUMOZOTT FÁJL ÍRÁS
• Képernyőre írás printf ( )
• Visszatérési értek kiírt karakterek darabszáma
• ( " formátum string " , adat )
• Fájlba írás fprintf ( )
• Visszatérési értek fáljba írt karakterek
darabszáma
• Mezőszélesség meghatározó
• Vezérlő karakterek is!

© Valkai Zoltán 2020 ÓE/KVK/MAI 34


FORMÁTUMOZOTT FÁJL ÍRÁS
#define FNEV27 "hifile27.dat"
int main()
{
FILE *fp27 ; int x = -21843 , y ; float f = 3.14159 ;
char k = '*' , kt [ ] = "Werner Heisenberg" ;
fp27 = fopen ( FNEV27 , "w" ) ;
if ( fp27 == NULL ) printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
printf ( "%i\n" , x ) ; y = fprintf ( fp27 , "%i\n" , x ) ; printf ( "y: %i\n" , y);
x = - x / 1000 ;
printf ( "%i\n" , x ) ; y = fprintf ( fp27 , "%i\n" , x ) ; printf ( "y: %i\n" , y);
printf ( "%10i\n" , x ) ; y = fprintf ( fp27 , "%10i\n" , x ) ; printf ( "y: %i\n" , y);
printf ( "%f\n" , f ) ; y = fprintf ( fp27 , "%f\n" , f ) ; printf ( "y: %i\n" , y);
printf ( "%c\n" , k ) ; y = fprintf ( fp27 , "%c\n" , k ) ; printf ( "y: %i\n" , y);
printf ( "%s\n" , kt ) ; y = fprintf ( fp27 , "%s\n" , kt ) ; printf ( "y: %i\n" , y);
fclose ( fp27 ) ;
fprintf( stdout , "\nEz is egy file iras volt\n\n" ) ; // ugye ?
}
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 35
FORMÁTUMOZOTT FÁJL OLVASÁS
#define FNEV28 "hifile28.dat"
int main()
{
FILE *fp28 ; int x = -21843 , y ; float f = 3.14159 ;
fp28 = fopen ( FNEV28 , "w+" ) ;
if ( fp28 == NULL ) printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
fprintf ( fp28 , "%i\n" , x ) ; fprintf ( fp28 , "%f\n" , f ) ;
fseek ( fp28 , 0 , SEEK_SET ) ;
f=x=0;
y = fscanf ( fp28 , "%i" , &x) ;
fscanf ( fp28 , "%f" , &f) ;
fclose ( fp28 ) ;
printf ( "y %i\n" , y ) ;
printf ( "x: %i\n" , x ) ;
printf ( "f: %f\n" , f ) ;
}
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 36
FORMÁTUMOZOTT FÁJL OLVASÁS
#define FNEV29 "hifile29.dat"
int main( )
{
FILE *fp29 ; int x = 0x40 ; fp29 = fopen ( FNEV29 , "w+" ) ;
if ( fp29 == NULL ) printf ( "A fajlt nem sikerult letrehozni\n" ) ;
else
{
for ( x = 0x5A ; x >= 0x41 ; x = x - 2 ) putc ( x , fp29 ) ;
fseek ( fp29 , 0 , SEEK_SET ) ;
while ( 1 )
{
x = getc ( fp29 ) ; if ( x < 0 ) break ; printf ( "%c " , x ) ;
}
fclose ( fp29 ) ; printf ( "\n\n" ) ;
} return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 37
KÖSZÖNÖM A FIGYELMET!

© Valkai Zoltán 2020 ÓE/KVK/MAI 38


PROGRAMOZÁS II.
9+. Előadás

FÁJL-KEZELÉS

© Valkai Zoltán 2020 ÓE/KVK/MAI 1


FELADAT információk

• Egyetlen programot kell írni, mert csak egy állomány


tölthető fel
• Minden egyes feladatot külön függvény valósítson
meg
• A forrásállományt ( main.c ) kell feltölteni
• 0 hibával forduló kódot kell feltölteni
• Csak a feladatban szereplő konstansok használhatók
• A program 0-val térjen vissza
• Nem tartalmazhat végtelen ciklust, címzési hibát
© Valkai Zoltán 2020 ÓE/KVK/MAI 2
1. FELADAT
Írjon és hívjon meg egy függvényt, amely alacsony
szinten binárisan írja fájlba a paraméterként átadott
egész típusú tömbből azokat az elemeket, amelyek 3-
mal oszthatók!
A függvény bemenő paraméterei az adatállomány
azonosítója, az adatokat tartalmazó tömb címe, és
a tömb elemeinek darabszáma legyenek.
Az adatállomány azonosítója "3NZH_1_01.bin" legyen!
A tömb elemeire mindig cím szerint hivatkozzon!
A függvény adja vissza a fájlba írt bájtok számát,
amit a main() függvényben decimálisan írasson
ki a képernyőre!
© Valkai Zoltán 2020 ÓE/KVK/MAI 3
A főprogram
#define fnev22 "3NZH_1_01.bin"
int fv22 ( char *fnev , int *tmb , int n ) ;
int main ( )
{
int y = 0 ;
char kt [ 21 ] = fnev22 ;
int it [ ] = { 272 , 156 , 210 , 342 , 182 , 132 , 240 , 110 , 380 , 306 };
y = fv22 ( kt , it , sizeof ( it ) / sizeof ( it [ 0 ] ) ) ;
printf("fv22: %i\n", y ) ;
return 0 ;
}

© Valkai Zoltán 2020 ÓE/KVK/MAI 4


A függvény
int fv22 ( char *fnev , int *tmb , int n )
{
int w = 0 , fh22 , db = n;
fh22 = open ( fnev, O_RDWR | O_BINARY | O_TRUNC | O_CREAT ,
S_IREAD | S_IWRITE ) ;
if ( fh22 < 0 ) w = fh22 ; // w = ?
else
{
while ( db )
{
if ( ! ( *tmb % 3 ) )
{
w = w + write ( fh22 , tmb , sizeof ( *tmb ) ) ;
}
db -- ;
tmb ++ ;
}
close ( fh22 ) ;
}
return w ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 5
2. FELADAT
Töltse le a „3NZH_2_01.bin" állományt a saját projektjének
munkakönyvtárába.
Írjon és hívjon meg egy függvényt, amely alacsony szinten, binárisan
megnyitja a fenti állományt!
A fájl egész típusú adatokat tartalmaz.
A függvény bemenő paramétere az adatállomány azonosítója
legyen.
A függvény összegezze a fájlban lévő adatokat, és ez legyen a
függvény visszatérési értéke!
A függvény visszatérési értéket a main ( )
függvényben írassa ki decimálisan a képernyőre!

© Valkai Zoltán 2020 ÓE/KVK/MAI 6


A program, ami mindkét függvényt tartalmazza
#define fnev22 "3NZH_1_01.bin"
int fv22 ( char *fnev , int *tmb , int n ) ;
int fv23 ( char *fnev ) ;
int main ( )
{
char kt23 [ ] = "3NZH_2_01.bin" , kt22 [ 21 ] = fnev22 ;
int y = 0 ;
int it [ ] = { 272 , 156 , 210 , 342 , 182 , 132 , 240 , 110 , 380 , 306 };
y = fv22 ( kt22 , it , sizeof ( it ) / sizeof ( it [ 0 ] ) ) ;
printf("fv22: %i\n", y ) ;
printf ( "fv23: %i\n" , fv23 ( kt23 ) ) ;
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 7
Az fv23() függvény
int fv23 ( char *fnev )
{
int w = 0 , fh23 , x ;
fh23 = open ( fnev, O_RDONLY | O_BINARY , S_IREAD | S_IWRITE);
if ( fh23 < 0 ) w = fh23 ;
else
{
while ( read ( fh23 , &x , sizeof ( x ) ) )
w=w+x;
close ( fh23 ) ;
}
return w ;
}

© Valkai Zoltán 2020 ÓE/KVK/MAI 8


Az fv22() függvény
int fv22 ( char *fnev , int *tmb , int n )
{
int w = 0 , fh22 , db = n;
fh22 = open ( fnev, O_RDWR | O_BINARY | O_TRUNC | O_CREAT ,
S_IREAD | S_IWRITE ) ;
if ( fh22 < 0 ) w = fh22 ; // w = ?
else
{
while ( db )
{
if ( ! ( *tmb % 3 ) )
w = w + write ( fh22 , tmb , sizeof ( *tmb ) ) ;
db -- ; tmb ++ ;
}
close ( fh22 ) ;
}
return w ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 9
KÖSZÖNÖM A FIGYELMET!

© Valkai Zoltán 2020 ÓE/KVK/MAI 10


PROGRAMOZÁS II.
10. Előadás

DINAMIKUS
MEMÓRIAKEZELÉS

© Valkai Zoltán 2020 ÓE/KVK/MAI 1


CPU MEMÓRIA
• Intel CPU

– Szegmentált memóriacímzés
– Minden program saját memóriaterületet kap

– CS: Code Segment


– DS: Data Segment
– ES: Extra Segment
– SS: Stack Segment

© Valkai Zoltán 2020 ÓE/KVK/MAI 2


MEMÓRIA
0000
STACK
GLOBÁLIS ADAT

main()
FÜGGVÉNYEK definiciói

HEAP
© Valkai Zoltán 2020 ÓE/KVK/MAI 3
MEMÓRIAFOGLALÁS
• C-ben program: 4 memóriaterület
– CODE
– GLOBAL
– STACK
– HEAP

– Statikus memóriaterület kiosztás fordításkor


– Előre ismert a szükséges memória mérete
– Rekurzió esetén nem/nehézkes
– Taskok memóriaigényét ismerni kell – statikus mem.
foglalás, használattól független
– Statikus memóriakezeléssel létrehozható
dinamikusnak tűnő stack és heap (halom)
© Valkai Zoltán 2020 ÓE/KVK/MAI 4
MEMÓRIAFOGLALÁS
• HEAP
– Nagyobb memóriaterület
– Programozó kezeli
– Programozó felelős érte
– Memóriaszivárgás
– Lassabb hozzáférés
– Hozzáférhetőség logikai blokkból

© Valkai Zoltán 2020 ÓE/KVK/MAI 5


TÖMBÖK
• Jellemzően statikusan jönnek létre
• Memóriafoglalás a program végéig
• Nagyméretű tömb → adatmemória statikus
foglalás
• Hivatkozás → statikus memóriában lévő
pointerrel
• Tömb a memóriában van
• Problémák
– Túlméretes tömb → nagy memóriafoglalás
– Alulméretezett tömb nem növelhető
© Valkai Zoltán 2020 ÓE/KVK/MAI 6
DINAMIKUS TÖMB
• HEAP-en jön létre
• Memóriaszivárgás miatt → felszabadítani
• Nyilvántartás a foglalt memóriablokkokról

• malloc ()
• calloc ()
• realloc ()
• free()

© Valkai Zoltán 2020 ÓE/KVK/MAI 7


MALLOC
• void * malloc ( int n ) ;
• n: foglalni kívánt bájtok száma
• n lehet szorzat is: elemszám*egy elem mérete
• Címet ad vissza
• Ha a visszatérési érték NULL → sikertelen

© Valkai Zoltán 2020 ÓE/KVK/MAI 8


CALLOC
• void * calloc ( int n , int adatlelem mérete) ;

• n: foglalni kívánt adatelemek darabszáma


• Adatelem mérete: egy elem mérete
• Címet ad vissza
• Ha a visszatérési érték NULL → sikertelen
• A teljes lefoglalt memória tartalmat 0-ázza

© Valkai Zoltán 2020 ÓE/KVK/MAI 9


REALLOC
• void* realloc ( void *ptr , int méret ) ;
• ptr: az eredetileg lefoglalt memóriablokk
kezdőcíme
• méret: a blokk új mérete bájlban, < , >
• Ha a visszatérési érték NULL → sikertelen
• Egyébként az új blokk kezdőcíme
• Ha nem volt törölve → memóriaszemét
• Ha törölve volt → 0

© Valkai Zoltán 2020 ÓE/KVK/MAI 10


FREE
• void free ( void *ptr ) ;
• Nincs visszatérési értéke
• A ptr kezdőcímű blokkot felszabadítja
• A blokk méretét nem kell megadni, azt az
operációs rendszer tartja nyilván
• A blokk tartalma teljesen elveszik
• Ha kellenek az adatok, kiírni fájlba
• A fejlesztői környezetben futtatott program a
végén automatikusan felszabadítja a foglalt M-t
• Egyébként memóriaszivárgás lehet
© Valkai Zoltán 2020 ÓE/KVK/MAI 11
FELADAT_1
• Legyen egy statikus tömb

• Foglaljunk 60 db int típusnak dinamikusan egy


tömböt

• Másoljuk át a statikus tömb elemeit

• Írjuk ki a képernyőre a teljes dinamikus tömböt

© Valkai Zoltán 2020 ÓE/KVK/MAI 12


PROGRAM_1
int main ( )
{
#define N0 60
int t [ ] = {2 , 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18 , 20 , 0 } ;
int *tp = NULL , *tdp , *kc , i ;
tp = t ; kc = malloc ( N0 * sizeof(int) ) ; tdp = kc ;
printf("\na dinamikus tomb kezdocime: %p\n\n" , kc ) ;
while ( *tp )
{
*tdp = *tp; tdp++ ; tp++ ;
}
for ( i = 0 , tdp = kc ; i < N0 ; i ++ , tdp++ ) printf ( "%20i" , *tdp ) ;
printf ( "\n\n" ) ;
free ( kc ) ;
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 13
© Valkai Zoltán 2020 ÓE/KVK/MAI 14
FELADAT_2

• Legyen egy statikus tömb

• Foglaljunk egy 40 elemű egész típusú dinamikus


tömböt; az érték nélküli elemek legyenek 0-k

• Másoljuk át a statikus tömb elemeit

• Írjuk ki a képernyőre a teljes dinamikus tömböt

© Valkai Zoltán 2020 ÓE/KVK/MAI 15


PROGRAM_2
int main()
{
#define N0 40
int t[] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 111 } ;
int *tp = NULL , *tdp = NULL , *kc = NULL , i , n ;
tp = t ; n = sizeof ( t ) / sizeof ( *t ) ;
kc = calloc ( N0 , sizeof ( int ) ) ; tdp = kc ;
while ( n )
{
*tdp = *tp; tdp++ ; tp++ ; n -- ;
}
for (i = 0 , tdp = kc ; i < N0 ; i ++ , tdp++ ) printf ( "%20i„ , *tdp ) ;
printf ( "\n\n" ) ;
free ( kc ) ;
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 16
© Valkai Zoltán 2020 ÓE/KVK/MAI 17
FELADAT_3
• Legyen egy statikus tömb
• Foglaljunk 20 db int típusnak dinamikusan egy
tömböt, a nem definiált elemek értéke legyen 0
• Másoljuk át a statikus tömb elemeit
• Írjuk ki a képernyőre a teljes dinamikus tömböt

• Változtassuk a dinamikus tömb méretét 40


eleműre
• Írjuk ki a képernyőre a teljes dinamikus tömböt

© Valkai Zoltán 2020 ÓE/KVK/MAI 18


PROGRAM_3
int main()
{
#define N0 20
#define N1 40
int t [ ] = {20 , 40 , 60 , 80 } ; int *tp = NULL , *tdp , *kc , i , n ;
n = sizeof ( t ) / sizeof ( *t ) ; tp = t ; kc = calloc ( N0 , sizeof(int) ) ;
tdp = kc ;
while ( n )
{ *tdp = *tp; tdp++ ; tp++ ; n-- ; }
for ( i = 0 , tdp = kc ; i < N0 ; i ++ , tdp++ ) printf ( "%20i" , *tdp ) ;
printf ( "\n\n" ) ;
kc = realloc ( kc , N1 * sizeof ( int ) ) ;
for ( i = 0 , tdp = kc ; i < N1 ; i ++ , tdp++ ) printf ( "%20i" , *tdp ) ;
free ( kc ) ;
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 19
© Valkai Zoltán 2020 ÓE/KVK/MAI 20
FELADAT_4
• Legyen egy statikus tömb
• Foglaljunk 32 db int típusnak dinamikusan egy
tömböt
• Másoljuk át a statikus tömb elemeit
• Írjuk ki a képernyőre a teljes dinamikus tömböt

• Változtassuk a dinamikus tömb méretét 20


eleműre
• Írjuk ki a képernyőre a teljes dinamikus tömböt

© Valkai Zoltán 2020 ÓE/KVK/MAI 21


PROGRAM_4
int main()
{
#define N0 32
#define N1 20
int t [ ] = { 32 , 34 , 36 , 38 , 40 } ; int *tp = NULL , *tdp , *kc , i , n ;
n = sizeof ( t ) / sizeof ( *t ) ; tp = t ; kc = malloc ( N0 * sizeof(int) ) ;
tdp = kc ;
while ( n )
{ *tdp = *tp; tdp++ ; tp++ ; n-- ; }
for ( i = 0 , tdp = kc ; i < N0 ; i ++ , tdp++ ) printf ( "%20i" , *tdp ) ;
printf ( "\n\n" ) ;
kc = realloc ( kc , N1 * sizeof ( int ) ) ;
for ( i = 0 , tdp = kc ; i < N1 ; i ++ , tdp++ ) printf ( "%20i" , *tdp ) ;
free ( kc ) ;
return 0 ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 22
© Valkai Zoltán 2020 ÓE/KVK/MAI 23
KÖSZÖNÖM A FIGYELMET!

© Valkai Zoltán 2020 ÓE/KVK/MAI 24


PROGRAMOZÁS II.
11. Előadás

DINAMIKUS ADATSZERKEZETEK

LÁNCOLT LISTÁK

© Valkai Zoltán 2020 ÓE/KVK/MAI 1


FOGALMAK
• Lista: adattételek lineáris, vagyis egymást követő
felsorolása
• Pl.: utazás előtt a következőket kell ellenőrizni
• Törölhetünk vagy hozzáadhatunk
• Adatfeldolgozás: a listába szervezett adatok
tárolása és feldolgozása
• Tömb: elemek között lineáris kapcsolat
• A lineáris kapcsolat a memóriában tárolt adatok
közötti fizikai kapcsolat ; nem az információra
• Tömb: sűrű lista vagy statikus adatszerkezet
© Valkai Zoltán 2020 ÓE/KVK/MAI 2
TÖRTÉNET
• 1955-56 RAND Corporation
• Information Processing Language - kutatás
• Korai mesterséges intelligencia program
fejlesztése
• Logic Theory Machine , General Problem Solver
• Sakkprogram
• 1957 február cikk és ábra
• Newell, Simon 1975 Turing díj
• Alapvető hozzájárulás a mesterséges
intelligenciához, a megismerés pszichológiája
© Valkai Zoltán 2020 ÓE/KVK/MAI 3
ELVEK

• A párhuzamos tömbök elvileg különböző típusú,


de összetartozó adatokat tárolhatnak
• Szerencsés, ha az adataink rendezett formában
tárolódnak
• A HEAP kellően nagy méretű
• Futás közben kellene az adathalmaz méretét
változtatni

© Valkai Zoltán 2020 ÓE/KVK/MAI 4


ELVEK
• A dinamikus tömb mérete változtatható
• Minden méretváltoztatás elem(ek) áthelyezését
is igényli, ha a rendezettség fenntartása feltétel
– Beszúrás
• Beszúrás esetén méretnövelés
• Elemek mozgatása
• Új elem beillesztése (felülírás)
– Törlés
• Elemek mozgatása
• Méretcsökkentés
• Folyamatosan sok adminisztrációs műveletet
igényel © Valkai Zoltán 2020 ÓE/KVK/MAI 5
?
• Lineárisan kell nekünk tárolni az adatokat?

• Tudjuk előre, hogy mennyi adatot szeretnénk


tárolni?
• Hogyan jutunk el a következő adathoz?
• Hol kezdődnek az adatok?
• Hol van vége az adathalmaznak?

• Mi lenne, ha a következő adat címét tárolnánk?

© Valkai Zoltán 2020 ÓE/KVK/MAI 6


ADATSZERKEZET

• Adatrész
• Tetszőleges számú és típusú mezőt tartalmazhat
• Egy szempont szerint rendezettség

adatrész
8 K

© Valkai Zoltán 2020 ÓE/KVK/MAI 7


ADATSZERKEZET

• Adatrész
• Tetszőleges számú és típusú mezőt tartalmazhat
• Egy szempont szerint rendezettség
• Címrész
• tetszőleges számú címmezőt tartalmazhat

8 K

adatrész címrész
© Valkai Zoltán 2020 ÓE/KVK/MAI 8
ADATSZERKEZET

• Adatrész
• Tetszőleges számú és típusú mezőt tartalmazhat
• Egy szempont szerint rendezettség
• Címrész
• tetszőleges számú címmezőt tartalmazhat

8 K 21 W

adatrész címrész adatrész címrész


© Valkai Zoltán 2020 ÓE/KVK/MAI 9
ADATSZERKEZET

• Adatrész
• Tetszőleges számú és típusú mezőt tartalmazhat
• Egy szempont szerint rendezettség
• Címrész
• tetszőleges számú címmezőt tartalmazhat

8 K 21 W 89 A ?

adatrész címrész adatrész címrész adatrész címrész


© Valkai Zoltán 2020 ÓE/KVK/MAI 10
ADATSZERKEZET

• Adatrész
• Tetszőleges számú és típusú mezőt tartalmazhat
• Egy szempont szerint rendezettség
• Címrész
• tetszőleges számú címmezőt tartalmazhat

8 K 21 W 89 A ?

adatrész címrész adatrész címrész adatrész címrész


Első mező szerint rendezett
© Valkai Zoltán 2020 ÓE/KVK/MAI 11
ADATSZERKEZET C-BEN
• Külöböző típusú adatok

• Struktúra
• Definíció
struct lista
{
int adat ;
char name [ 50 ] ;
struct ???????? ;
}; © Valkai Zoltán 2020 ÓE/KVK/MAI 12
ADATSZERKEZET C-ben

struct llista typedef struct llista


{ {
int adat ; int adat ;
char id [ 50 ] ; char id [ 50 ] ;
struct llista *next ; struct llista *next ;
}; } lista ;

© Valkai Zoltán 2020 ÓE/KVK/MAI 13


MŰVELETEK

• Lista létrehozása

• Adat beillesztése a listába


• Adat törlése a listából
• Adat keresése a listában
• Lista bejárása

• Lista törlése
© Valkai Zoltán 2020 ÓE/KVK/MAI 14
LÉTREHOZÁS
• Honnan tudjuk, hogy van vagy nincs lista?
• Az adatszerkezet legyen egyszerű
– Csak egy egész típusú adatot tárolunk

typedef struct llista


{
int adat ;
struct llista *next ;
} list ;
© Valkai Zoltán 2020 ÓE/KVK/MAI 15
ADATSZERKEZET

NULL

© Valkai Zoltán 2020 ÓE/KVK/MAI 16


ADATSZERKEZET

666 NULL

© Valkai Zoltán 2020 ÓE/KVK/MAI 17


LÉTREHOZÁS
int main ( )
{
void *root = NULL ;
list *aktualis = NULL ;
aktualis = malloc ( sizeof ( list ) ) ;
aktualis -> adat = 666 ;
aktualis -> next = NULL ;
root = aktualis ; //
printf ( "root: %p\n" root ) ;
printf ( "adat: %i\n " , aktualis -> adat ) ;
printf ( "kov_cim: %p\n" , aktualis -> next ) ;

free ( aktualis ) ;
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 18
PROBLÉMÁK 1
• A listát folyamatosan szeretnénk felépíteni
• A lista végét a NULL címrész jelzi
• Az új adat rendezett lista esetén
– Első elé
– Első mögé
• A root pointer tartalmazza a lista kezdőcímét

© Valkai Zoltán 2020 ÓE/KVK/MAI 19


PROBLÉMÁK 1
• A listát folyamatosan szeretnénk felépíteni
• A lista végét a NULL címrész jelzi
• Az új adat rendezett lista esetén
– Első elé
– Első mögé
• A root pointer tartalmazza a lista kezdőcímét


• root

© Valkai Zoltán 2020 ÓE/KVK/MAI 20


PROBLÉMÁK 1
• A listát folyamatosan szeretnénk felépíteni
• A lista végét a NULL címrész jelzi
• Az új adat rendezett lista esetén
– Első elé
– Első mögé
• A root pointer tartalmazza a lista kezdőcímét
666 NULL

• root aktuális
• Először mindig az új adatszerkezet mezőit kell
feltölteni
© Valkai Zoltán 2020 ÓE/KVK/MAI 21
PROBLÉMÁK 1
• A listát folyamatosan szeretnénk felépíteni
• A lista végét a NULL címrész jelzi
• Az új adat rendezett lista esetén
– Első elé
– Első mögé
• A root pointer tartalmazza a lista kezdőcímét
666 NULL

• root aktuális
• A root pointer értékét a memória felszabadítás
miatt őrizni kell
© Valkai Zoltán 2020 ÓE/KVK/MAI 22
PROBLÉMÁK 2
• Két elemű rendezett lista esetén

666 1045 NULL

• root előző aktuális

• Először mindig az új adatszerkezet mezőit kell


feltölteni

© Valkai Zoltán 2020 ÓE/KVK/MAI 23


PROBLÉMÁK 2
• Két elemű rendezett lista esetén

666 1045 NULL

• root előző aktuális

666 NULL 101

© Valkai Zoltán 2020 ÓE/KVK/MAI 24


PROBLÉMÁK 2
• Két elemű rendezett lista esetén

666 1045 NULL

• root előző aktuális

666 NULL 101

• Először mindig az új adatszerkezet mezőit kell


feltölteni
© Valkai Zoltán 2020 ÓE/KVK/MAI 25
PROBLÉMÁK 3
• Három vagy több elemű lista esetén az új elem
666 1045

101
• Itt már sok pointer kell ?
• Az új (3.) adat már 3 helyre kerülhet
– A legelső adat elé
– A meglévő két adat közé
– Az utolsó adat után
• Akarunk sokat programozni ?
• Minden egyes programsor a lehetséges hibák
számát növeli © Valkai Zoltán 2020 ÓE/KVK/MAI 26
MEGOLDÁS
• Ne dolgozzunk annyit
• A felesleges programírást csökkentsük
• Az előforduló hibák számát csökkentsük
• Akkor a ellenőrzés (debug) is kevesebb lesz

• Megoldás:
• Egészítsük ki a listát két elemmel (Sentinel Node)
• Az egyik legyen minden előforduló értéknél
kisebb
• A másik minden lehetséges értéknél nagyobb
© Valkai Zoltán 2020 ÓE/KVK/MAI 27
KÖVETKEZMÉNY
• Csak két elem közé történő beillesztést kell
megvalósítani
• Csak két elem közül történő törlést kell lekódolni
• Az üres lista eleve két elemet tartalmaz
• A lista törlésekor nem csak a hasznos elemek
foglalását, hanem a két szélső elemet is törölni
kell
• A rendezési szempont szerinti struktúra mezőt,
kulcsnak nevezzük
• Egész típus esetén limits.h INT_MAX
© Valkai Zoltán 2020 ÓE/KVK/MAI 28
Strukrúra definíció
#include <limits.h>
typedef struct llista
{
int adat ;
struct llista *next ;
} list ;

int listatorol ( void *kc ) ;


int listakiir ( void *kc ) ;
list *beszur ( list *kc , int x ) ;
© Valkai Zoltán 2020 ÓE/KVK/MAI 29
Főprogram
int main()
{
int x = 1 , y = 0 ; void *root = NULL ; list *aktualis = NULL , *elozo = NULL ;
aktualis = malloc (sizeof ( list ) ) ; root = aktualis ;
aktualis -> adat = INT_MIN ; aktualis -> next = NULL ;
elozo = aktualis ;
aktualis = malloc (sizeof ( list ) ) ; aktualis -> adat = INT_MAX ;
aktualis -> next = NULL ; elozo -> next = aktualis ;
while ( 1 )
{
printf ( "\nuj adat: " ) ; scanf ( "%i" , &x ) ;
if ( !x ) break ; beszur ( root , x ) ;
}
y = listakiir ( root ) ;
printf ( "\nA lista elemszama: %i\n" , y ) ;
y = listatorol ( root ) ;
printf ( "\nA listabol torol elemszam: %i\n" , y ) ;
return 0 ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 30
Adatokat kiíró függvény
int listakiir( void *kc )
{
int n = 0 ;
list *akt = kc ;
while ( 1 )
{
printf ( "%20i\n" , akt -> adat ) ;
n++ ;
if (( akt -> next ) == NULL ) break ;
akt = akt -> next ;
}
return n ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 31
Memória felszabadítás
int listatorol ( void *kc )
{
int n = 0 ;
list *akt = NULL , *elozo = kc ;
while(1)
{
akt = elozo -> next ;
free ( elozo ) ;
elozo = akt ;
n++ ;
if ( ( elozo -> next ) == NULL ) break ;
}
free ( elozo ) ;
n++ ;
return n ;
} © Valkai Zoltán 2020 ÓE/KVK/MAI 32
Adat beszúrás
list *beszur ( list *kc , int x )
{
list *elozo = kc , *akt = NULL ;
while ( elozo -> next -> adat < x)
elozo = elozo -> next ;

akt = malloc ( sizeof ( list ) ) ;


if (akt == NULL )
return NULL ;
akt -> next = elozo -> next ;
akt -> adat = x ;
printf ( "a listaelem kezdocime:%20p" , akt ) ;
elozo -> next = akt ;
return elozo ;
}
© Valkai Zoltán 2020 ÓE/KVK/MAI 33
KÉTSZERESEN LÁNCOLT LISTA
• A címrész nem egy, hanem két mutatót tartalmaz
• Hátrány:
– két címrészt kell kezelni, biztosan legalább eggyel
több mutató kell
• Előny:
– bármelyik irányba tudunk lépni beszúrás / törlés
– az egyszeresen láncolt lista mindig csak az elejétől
járható be
– Sentinel Node ( strázsa ) itt is lehet
• De… egyszeres lista
– ha az adat az aktuális listaelemnél nagyobb →
kezdhetjük onnan is
© Valkai Zoltán 2020 ÓE/KVK/MAI 34
KÉTSZERESEN LÁNCOLT LISTA

101 NULL NULL

Először mindig az új adatszerkezet mezőit kell feltölteni

© Valkai Zoltán 2020 ÓE/KVK/MAI 35


KÉTSZERESEN LÁNCOLT LISTA

101 NULL 718 NULL

Először mindig az új adatszerkezet mezőit kell feltölteni

© Valkai Zoltán 2020 ÓE/KVK/MAI 36


KÉTSZERESEN LÁNCOLT LISTA

101 NULL 718 NULL

307

Először mindig az új adatszerkezet mezőit kell feltölteni

© Valkai Zoltán 2020 ÓE/KVK/MAI 37


KÉTSZERESEN LÁNCOLT LISTA

101 NULL 718 NULL

307

Először mindig az új adatszerkezet mezőit kell feltölteni

© Valkai Zoltán 2020 ÓE/KVK/MAI 38


KÉTSZERESEN LÁNCOLT LISTA

101 NULL 718 NULL

307

© Valkai Zoltán 2020 ÓE/KVK/MAI 39


KÉTSZERESEN LÁNCOLT LISTA

101 NULL 718 NULL

307

© Valkai Zoltán 2020 ÓE/KVK/MAI 40


KÖSZÖNÖM A FIGYELMET!

© Valkai Zoltán 2020 ÓE/KVK/MAI 41

You might also like