Programozás Alapjai (2008)

Slides:



Advertisements
Hasonló előadás
C# nyelvi áttekintő A „Programozás C# nyelven (Illés Zoltán)”
Advertisements

 Megfigyelhető, hogy amikor több elem közötti összehasonlítás történik, akkor szükség van egyszerre több értékre is, főleg akkor, ha ezek az értékek jóval.
Összetett adattípusok 8/b tétel
LFüggvények Alkalmazott Informatikai Tanszék MŰSZAKI INFORMATIKA dr.Dudás László 20./0. lFüggvények deklarációja és prototípusa lA függvénydefiníció lHivatkozás.
1 Hernyák Zoltán Programozási Nyelvek II. Eszterházy Károly Főiskola Számítástudományi tsz.
2009 Bevezetés a programozásba Krankovits Melinda.
Az új történelem érettségiről és eredményeiről augusztus Kaposi József.
© Kozsik Tamás Beágyazott osztályok A blokkstrukturáltság támogatása –Eddig: egymásba ágyazható blokk utasítások Osztálydefiníciók is egymásba.
A tételek eljuttatása az iskolákba
Dinamikus tömbök.
Csala Péter ANDN #4. 2 Tartalom  C# - ban előre definiált típusok  Változók  Változókkal műveletek  Elágazás  Ciklus.
Bevezetés a Java programozásba
Bevezetés a Java programozásba
11. előadás (2005. május 10.) A make segédprogram Alacsony szintű műveletek és bitmezők Fájl, katalógus rendszer hívások 1.
6. előadás (2005. április 5.) Struktúrák Úniók Új adattípus definíálása Dinamikus memória foglalás 1.
7. előadás (2005. április 12.) Láncolt lista File kezelés 1.
5. előadás (2005. március 22.) Függvények definíciója, deklarációja, hívása Enumerációs adattípus 1.
4. előadás (2005. március 8.) Pointerek Pointer aritmetika
2. Gyakorlat Követelmények / „C” ismétlés
UNIVERSITY OF SZEGED D epartment of Software Engineering UNIVERSITAS SCIENTIARUM SZEGEDIENSIS Programozás II. 9. Gyakorlat Alap file műveletek.
Programozás II. 3. Gyakorlat C++ alapok.
UNIVERSITY OF SZEGED D epartment of Software Engineering UNIVERSITAS SCIENTIARUM SZEGEDIENSIS Programozás II. 6. Gyakorlat const, static, dinamikus 2D.
Mutatók, tömbök, függvények
Borland C/C++ mintapéldák
Készítette: Pető László
1. IS2PRI2 02/96 B.Könyv SIKER A KÖNYVELÉSHEZ. 2. IS2PRI2 02/96 Mi a B.Könyv KönyvelésMérlegEredményAdóAnalitikaForintDevizaKönyvelésMérlegEredményAdóAnalitikaForintDeviza.
Miskolci Egyetem Informatikai Intézet Általános Informatikai Tanszé k Pance Miklós Adatstruktúrák, algoritmusok előadásvázlat Miskolc, 2004 Technikai közreműködő:
LDinamikus tömbök, kétdimenziós tömbök Alkalmazott Informatikai Tanszék MŰSZAKI INFORMATIKA dr.Dudás László 21./0. lVektorok létrehozása futásidőben, dinamikusan.
1 Programozás alapjai GEIAL312B (ANSI C) BSc (Bachelor of Science) / Alap képzés 2005/2006. őszi félév Miskolci Egyetem Általános Informatikai Tanszék.
C++ Alapok, első óra Elemi típusok Vezérlési szerkezetek
Sárgarépa piaca hasonlóságelemzéssel Gazdaság- és Társadalomtudományi kar Gazdasági és vidékfejlesztési agrármérnök I. évfolyam Fekete AlexanderKozma Richárd.
További vektor, mátrix algoritmusok
PHP I. Alapok. Mi a PHP? PHP Hypertext Preprocessor Szkriptnyelv –Egyszerű, gyors fejlesztés –Nincs fordítás (csak értelmező) Alkalmazási lehetőségek:
Merre tovább? Tapasztalatok a kétszintű latin nyelvi érettségiről.
Programozás Operátorok C# -ban.
C nyelv utasításai.
Összetett adattípusok
Programozási Nyelvek (C++) Gyakorlat Gyak 02.
Logikai szita Izsó Tímea 9.B.
Struktúra nélküli adatszerkezetek
Adatszerkezetek 1. előadás
Összetett adattípusok
Operátorok Értékadások
Készítette: Csíki Gyula
1 Hernyák Zoltán Programozási Nyelvek II. Eszterházy Károly Főiskola Számítástudományi tsz.
1 Hernyák Zoltán Web: Magasszintű Programozási Nyelvek I. Eszterházy.
A klinikai transzfúziós tevékenység Ápolás szakmai ellenőrzése
UNIVERSITY OF SZEGED D epartment of Software Engineering UNIVERSITAS SCIENTIARUM SZEGEDIENSIS Programozás II. 4. Gyakorlat Függvény paraméterek, dinamikus.
Egyenesvonalú (lineáris) adatszerkezetek
Objektum orientált programozás
Objektum orientált programozás
Objektum orientált programozás
1. Melyik jármű haladhat tovább elsőként az ábrán látható forgalmi helyzetben? a) A "V" jelű villamos. b) Az "M" jelű munkagép. c) Az "R" jelű rendőrségi.
> aspnet_regiis -i 8 9 TIPP: Az „Alap” telepítés gyors, nem kérdez, de később korlátozhat.
Függvények a C nyelvben 1 Függvényeket a következő esetekben szokás írni: Ha ugyanazt a tevékenységet többször is el kell végeznünk ugyanolyan típusú,
C Programozási alapok.
CUDA C/C++ programozás CUDA C bevezetés A segédanyag készítése a TÁMOP A/ Nemzeti Kiválóság Program című kiemelt projekt keretében.
1 Az igazság ideát van? Montskó Éva, mtv. 2 Célcsoport Az alábbi célcsoportokra vonatkozóan mutatjuk be az adatokat: 4-12 évesek,1.
UNIVERSITY OF SZEGED D epartment of Software Engineering UNIVERSITAS SCIENTIARUM SZEGEDIENSIS Programozás I. 3. gyakorlat.
Programtervezés, programozás I. 2.5 tömbök,stringek
Struktúrák a C nyelvben 1 Akkor használjuk, ha az egy egyedre jellemző különböző típusú adatokat együtt akarjuk tárolni. Lényegében típusdeklaráció. A.
1 Függvények használata – az első függvénynél a formulát háromszor be kell írni, rendre az x, x+h, x-h argumentumokkal, – a második függvénynél az új (feltételes.
Függvények, mutatók Csernoch Mária. Függvények függvény definíciója az értelmezési tartomány tetszőleges eleméhez hozzárendel egy értéket –függvény helyettesítési.
TÁMOP /1-2F Informatikai gyakorlatok 11. évfolyam Alapvető programozási tételek megvalósítása Czigléczky Gábor 2009.
Kifejezések C#-ban.
Változók.
Informatikai gyakorlatok 11. évfolyam
A struct, az union és az enum típus
JAVA programozási nyelv NetBeans fejlesztőkörnyezetben I/13. évfolyam
Függvénysablonok használata
Előadás másolata:

Programozás Alapjai (2008) Halmaz típus A programozásban számtalan formában előfordulhatnak halmazok és halmazokkal végzett műveletek. Legyen U egy egész típus, amit univerzumnak nevezünk. Tekintsük azt az értékhalmazt, amelynek elemei az U univerzum részhalmazai, ez lesz a Halmaz(U) új adattípus értékhalmaza. Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei (A Halmaz(U) adattípusra a továbbiakban a Halmaz azonosítót használjuk.) Üresít( <- H : Halmaz); A művelet végrehajtása után a H változó értéke az üres halmaz. Bővít( <-> H : Halmaz; -> x : U); A művelet a H változó értékéhez hozzáveszi az x elemet. Töröl( <-> H : Halmaz; -> x : U); A művelet a H változó értékéből törli az x elemet. Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Eleme( -> x : U; -> H : Halmaz) : bool; A függvényművelet akkor és csak akkor ad igaz értéket, ha x eleme a H halmaznak. Egyesítés( -> H1,H2 : Halmaz; <- H : Halmaz); A művelet eredményeként a H változó értéke a H1 és H2 halmaz egyesítése lesz. Metszet( -> H1,H2 : Halmaz; <- H : Halmaz); A művelet eredményeként a H változó értéke a H1 és H2 halmaz közös része lesz. Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Különbség( -> H1,H2 : Halmaz; <- H : Halmaz); A művelet eredményeként a H változó azokat és csak azokat az x in U értékeket tartalmazza, amelyre x in H1, de x nem eleme a H2 halmaznak. Egyenlő( -> H1,H2 : Halmaz) : bool; Az egyenlőség relációs művelet. Rész( -> H1,H2 : Halmaz) : bool; Akkor és csak akkor ad igaz értéket, ha a H1 halmaz minden eleme a H2 halmaznak is eleme. Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Ürese( -> H : Halmaz) : bool; Akkor és csak akkor ad igaz értéket, ha H értéke az üres halmaz. Értékadás( <- H1, -> H2 : Halmaz); A művelet hatására a H1 változó felveszi a H2 értékét. Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz megvalósítása A C nyelvben a halmaznak nincs nyelvi megvalósítása. A halmazok reprezentálásához induljunk ki abból, hogy tetszőleges U univerzum esetén az U részhalmazai megadhatók karakterisztikus függvényükkel. Ha H az U részhalmaza, akkor karakterisztikus függvénye: kH : U ---> {0,1}, kH(x)=1 <==> x in H Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz megvalósítása A karakterisztikus függvények és U részhalmazai között kölcsönösen egyértelmű megfeleltetést ad az előbbi összefüggés. Ha U egész típus, a karakterisztikus függvényeket meg tudnánk valósítani a bool[U] típussal, de ez nem hatékony megoldás, mert U minden eleméhez a logikai bool típus megvalósításától függően legalább egy (de inkább 4) byte szükséges. Az 1 bit/elem hatékonyságot el is tudjuk viszont érni, a bitműveletek segítségével. Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz megvalósítása A módszer lényege, hogy például egy int típusú változóban 32 bitet tárolhatunk, azaz egy 32 elemű kis halmaz reprezentálására ideális. vagy inkább: Melyik jobb? 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 31 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz megvalósítása Az egyes biteket a bitműveletekkel érhetjük el. A bit törlése, beállítása és lekérdezése az &, | és ~ műveletek segítségével történik. 31 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 31 1 = 1 << 0 31 1 = 1 << 1 31 1 = 1 << 31 Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz megvalósítása Nagyobb halmazt összerakhatunk kis halmazokból a következőképpen. Logikailag Fizikailag Kishalmaz sorszám K-1 ... K-1 ... 2K-1 ... K 1 K-1 ... (i+1)K-1 ... N ... iK i=(N / K) K-1 ... (N % K) ... MK-1 ... (M-1)K M-1 K-1 ... Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz megvalósítása Képezzük az alábbi típusokat: #define K (8*sizeof(KisHalmaz)) #define M ??? #define H_MAX (M*K) typedef int KisHalmaz; typedef KisHalmaz Halmaz[M]; Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz megvalósítása Minden x természetes szám egyértelműen meghatározott az (x / K, x % K) számpárral. Tehát x akkor és csak akkor eleme a H változó által reprezentált halmaznak, ha teljesül, hogy a H[x / K] KisHalmaznak eleme az (x % K), azaz a megfelelő KisHalmaz megfelelő bitje 1. Az univerzum halmaz ekkor a 0..H_MAX-1 intervallum lesz. Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Üresít( <- H : Halmaz); void Uresit(Halmaz H) { long int i; for(i = 0; i < M; ++i) H[i] = 0; } Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Bővít( <-> H : Halmaz; -> x : U); void Bovit(Halmaz H, unsigned long int x) { if(x < H_MAX) H[x / K] |= (1 << (x % K)); } Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Töröl( <-> H : Halmaz; -> x : U); void Torol(Halmaz H, unsigned long int x) { if(x < H_MAX) H[x / K] &= ~(1 << (x % K)); } Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Eleme( -> x : U; -> H : Halmaz) : bool; int Eleme(unsigned long int x, Halmaz H) { return (x < H_MAX) && (H[x / K] & (1 << (x % K))); } Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Egyesítés( -> H1,H2 : Halmaz; <- H : Halmaz); void Egyesites(Halmaz H1, Halmaz H2, Halmaz H) { long int i; for(i = 0; i < M; ++i) H[i] = H1[i] | H2[i]; } Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Metszet( -> H1,H2 : Halmaz; <- H : Halmaz); void Metszet(Halmaz H1, Halmaz H2, Halmaz H) { long int i; for(i = 0; i < M; ++i) H[i] = H1[i] & H2[i]; } Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Különbség( -> H1,H2 : Halmaz; <- H : Halmaz); void Kulonbseg(Halmaz H1, Halmaz H2, Halmaz H) { long int i; for(i = 0; i < M; ++i) H[i] = H1[i] & ~(H2[i]); } Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Egyenlő( -> H1,H2 : Halmaz) : bool; int Egyenlo(Halmaz H1, Halmaz H2) { long int i; for(i = 0; (i<M) && (H1[i]==H2[i]); ++i); return i==M; } Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Rész( -> H1,H2 : Halmaz) : bool; int Resz(Halmaz H1, Halmaz H2) { long int i; for(i = 0; (i<M) && !(H1[i] & ~(H2[i])); ++i); return i==M; } Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Ürese( -> H : Halmaz) : bool; int Urese(Halmaz H) { long int i; for(i = 0; (i<M) && !(H[i]); ++i); return i==M; } Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz műveletei Értékadás( <- H1, -> H2 : Halmaz); void Ertekadas(Halmaz H1, Halmaz H2) { long int i; for(i = 0; i < M; ++i) H1[i] = H2[i]; } Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz típus Azáltal, hogy az univerzum egész típus, lehetővé válik a diszkrét ismétléses vezérlés megfogalmazása ezekre a halmazokra. Legyen Umin, Umax az univerzum minimális illetve maximális eleme. x= Umin -> Umax x in H i n M x in H M Programozás Alapjai (2008)

Programozás Alapjai (2008) Halmaz típus Ebben az esetben tehát a diszkrét ismétléses vezérlés megvalósítása C nyelven: for(x=Umin; x<=Umax; ++x) { if(Eleme(x,H)) { M; } Programozás Alapjai (2008)

Programozás Alapjai (2008) Klikkek Problémafelvetés: Egy közösségben az emberek közötti barátsági kapcsolat alapján meg kell határoznunk a klikkeket. Specifikáció: Sorszámozzuk 1-től N-ig az embereket és olvassuk be az R barátsági relációt. Input: Számpárokat olvasunk be. Az input vége: 0 0. Az i,j pár azt jelenti, hogy az i. személy barátságban van a j. személlyel. Output: Soroljuk fel a baráti csoportokat. Programozás Alapjai (2008)

Programozás Alapjai (2008) Klikkek Algoritmustervezés: A matematika nyelvén: Tegyük fel, hogy a barátság reflexív, szimmetrikus és tranzitív reláció. Meg kell határozni az R relációt tartalmazó legszűkebb ekvivalencia reláció szerinti osztályozást. Induljunk ki abból, hogy minden személy csak saját magával van barátságban; tehát képezzük az {i} egyelemű halmazokat. Ha az input számpárokat valameddig feldolgozva meghatároztuk az osztályozást, a következő (i,j) számpárt beolvasva össze kell vonni azt a két részhalmazt, amelybe i illetve j tartozik, hisz mindenki, aki i-vel barátságban van, az barátságban van mindenkivel, akivel j barátságban van. Programozás Alapjai (2008)

Programozás Alapjai (2008) Klikkek Az algoritmus C megvalósítása tehát az absztrakt Halmaz(U) adattípus műveleteivel. /* A beolvasott R relációt tartalmazó legszűkebb ekvivalencia reláció szerinti osztályozást határozzuk meg. Vázlat, nem fordítható C program. 2006. Augusztus 14. Gergely Tamás, gertom@inf.u-szeged.hu */ #include <stdio.h> #define N 10 /* maximális elemszám */ typedef Halmaz(U) Halmaz; /* EZ ÍGY NEM C !!!*/ >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) Klikkek int main() { Halmaz H[N]; unsigned short i,j, ti,tj; for(i = 1; i <= N; ++i) { /* inicializálás */ Uresit(H[i-1]); Bovit(H[i-1], i); } printf("Kérem a relációban lévő számpárokat!\n"); scanf("%hd%hd%*[^\n]", &i, &j); getchar(); >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) Klikkek while(i != 0) { for(ti=0; !Eleme(i, H[ti]); ti++); /* amelyik H[ti] halmazban van i ? */ for(tj=0; !Eleme(j, H[tj]); tj++); /* amelyik H[tj] halmazban van j ? */ if(ti != tj) { /* H[ti] és H[tj] összevonása */ Egyesites(H[ti], H[tj], H[ti]); Uresit( H[tj] ); } scanf("%hd%hd%*[^\n]", &i, &j); getchar(); >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) Klikkek printf("Az osztályok: \n"); for (i = 1; i <= N; i++) { /* az osztályok kiíratása */ if (! Urese(H[i - 1])) { for (j = 1; j <= N; j++) { if (Eleme(j, H[i - 1])) printf("%4hd,", j); } putchar('\n'); Programozás Alapjai (2008)

Programozás Alapjai (2008) Klikkek Mivel N < 32, elég egy KisHalmaz. /* A beolvasott R relációt tartalmazó legszűkebb ekvivalencia reláció szerinti osztályozást határozzuk meg. 1997. December 6. Dévényi Károly, devenyi@inf.u-szeged.hu 2006. Augusztus 14. Gergely Tamás, gertom@inf.u-szeged.hu */ #include <stdio.h> #define N 10 /* maximális elemszám */ typedef int Halmaz; /* N kicsi, ezért elegendő az int */ >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) Klikkek main(int argc, char *argv[]) { Halmaz H[N]; unsigned short i, j, ti, tj; for (i = 1; i <= N; i++) /* inicializálás */ H[i - 1] = 1 << i; printf("Kérem a relációban lévő számpárokat!\n"); scanf("%hd%hd%*[^\n]", &i, &j); getchar(); >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) Klikkek while (i != 0) { /* While */ for(ti = 0; ((1 << i) & H[ti]) == 0; ti++); /* azon ti index keresése, amelyik H[ti] halmazban van i */ for(tj = 0; ((1 << j) & H[tj]) == 0; tj++); /* azon tj index keresése, amelyik H[tj] halmazban van j */ if (ti != tj) { /* H[ti] és H[tj] összevonása */ H[ti] |= H[tj]; H[tj] = 0; } scanf("%hd%hd%*[^\n]", &i, &j); getchar(); >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) Klikkek printf("Az osztályok: \n"); for (i = 1; i <= N; i++) { /* az osztályok kiíratása */ if (H[i - 1] != 0) { for (j = 1; j <= N; j++) { if (j < 32 && ((1 << j) & H[i - 1]) != 0) printf("%4hd,", j); } putchar('\n'); Programozás Alapjai (2008)

Programozás Alapjai (2008) Prímszámok Problémafelvetés: Határozzuk meg az adott N természetes számnál nem nagyobb prímszámokat. Specifikáció: Az N legyen konstans. Input: Nincs Output: Soroljuk fel a prímszámokat N-ig Programozás Alapjai (2008)

Programozás Alapjai (2008) Prímszámok Algoritmustervezés: A jól ismert Erathosztenészi szita algoritmust valósítjuk meg. A halmazt kezdetben feltöltjük az egynél nagyobb páratlan számokkal. Megkeressük a halmaz még nem feldolgozott legkisebb elemét (ez prímszám lesz) és töröljük a többszöröseit. Az előző pontot addig ismételjük, míg el nem érjük az N gyökét. Az eredményhalmaz csak a prímszámokat fogja tartalmazni. Programozás Alapjai (2008)

Programozás Alapjai (2008) Prímszámok /* * Határozzuk meg az adott N természetes számnál nem nagyobb * prímszámokat. * Készítette: Dévényi Károly, devenyi@inf.u-szeged.hu * 1997. December 6. * Módosította: Gergely Tamás, gertom@inf.u-szeged.hu * 2006. Augusztus 15. */ #include <stdio.h> #define K (8*sizeof(KisHalmaz)) #define M 100 #define N (M*K) typedef int KisHalmaz; typedef KisHalmaz Halmaz[M]; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) Prímszámok int main() { Halmaz szita; KisHalmaz kicsi; /* a szita inicializálásához */ long int p,s,lepes,i,j; kicsi = 0; /* a szita inicializálása */ for(i = 0; i <= ((K-1) / 2); ++i) { kicsi |= (1 << (2*i+1)); } for(i = 0; i < M; ++i) { szita[i] = kicsi; szita[0] &= ~2; /* 2 == (1 << 1) */ szita[0] |= 4; /* 4 == (1 << 2) */ >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) Prímszámok p = 3; /* az első szitálandó prím */ while(p*p < N) { /* P többszöröseinek kiszitálása */ lepes = 2 * p; /* lépésköz = 2*p */ s = p*p; /* s az első többszörös */ while(s < N) { szita[s / K] &= ~(1 << (s % K)); s += lepes; } do { /* a következő prím keresése */ p += 2; } while( (p < N) && ! (szita[p / K] & (1 << (p % K))) ); >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) Prímszámok j = 0; /* a prímszámok kiíratása képernyőre */ printf("A prímszámok %d-ig:\n", N); for(p = 2; p < N; ++p) { if(szita[p / K] & (1 << (p % K))) { printf("%8d", p); if(++j == 9) { j = 0; putchar('\n'); } Programozás Alapjai (2008)

Programozás Alapjai (2008) Dinamikus tömb típus Ha a programban deklarálunk egy tömböt, azzal az lehet a baj, hogy a méretét fordítási időben meg kell adni. Ez viszont nem mindig ismert, így előfordulhat, hogy a tömb számára kevés helyet foglaltunk, de az is, hogy feleslegesen sokat. A megoldás: tömb helyett pointert deklarálunk, és ha tudjuk a kívánt méretet, memóriát már a megfelelő számú elemnek foglalunk. Programozás Alapjai (2008)

Programozás Alapjai (2008) Dinamikus tömb típus Mivel a pointert tömbként kezelhetjük, a programban kódjában ez semmilyen más változást nem eredményez: int tomb[MAX]; … for(i=0; i<n; ++i) { tomb[i]=i; } int *tomb; … tomb=malloc(n*sizeof(int)); for(i=0; i<n; ++i) { tomb[i]=i; } free(tomb); Programozás Alapjai (2008)

Programozás Alapjai (2008) Flexibilis tömb típus A C nyelvben lehetőség van arra, hogy egy pointer számára már lefoglalt memóriaterület méretét megváltoztassuk. A void *realloc(void *ptr, size_t size) függvény a ptr által mutatott területet méretezi (és ha kell mozgatja) át. Ez sem használható viszont, ha a memória túlságosan „széttöredezett”, azaz nincsennek benne nagy egybefüggő részek. Programozás Alapjai (2008)

Programozás Alapjai (2008) Flexibilis tömb típus Egy megoldás a problémára a flexibilis tömb adattípus, ami a dinamikus tömb általánosítása. Ennek van olyan művelete amivel az indextípus felső határát módosíthatjuk, ezáltal változó elemszámú sorozatokat kezelhetünk, továbbá a megvalósítása kis méretű tömbökkel dolgozik. Adott E elemtípus esetén a flexibilis tömb (FTömb) adattípus értékhalmaza az összes A : 0..N ---> E függvény. Programozás Alapjai (2008)

Flexibilis tömb műveletei Kiolvas( -> A:FTömb; -> i:int; <- X:E) Az A függvény értékének kiolvasása Módosít( <-> A:FTömb; -> i:int; -> X:E) Az A függvény értékének módosítása X=Y értékadás, ha X és Y FTömb típusú változók. Létesít( <-> A:FTömb; -> N:int) N elemű flexibilis tömböt létesít. Megszüntet( <-> A:FTömb) Törli az A flexibilis tömbhöz foglalt memóriát. Programozás Alapjai (2008)

Flexibilis tömb műveletei Felső( -> A:FTömb): int A felső határ lekérdezése Növel( -> A : FTömb; d:int) Az aktuális indextípus felső határát a d értékkel növeli. Csökkent( -> A : FTömb; d:int) Az aktuális indextípus felső határát a d értékkel csökkenti. Programozás Alapjai (2008)

Flexibilis tömb megvalósítása A megvalósításhoz válasszunk egy L konstanst. L elemszámú dinamikussá tett tömbökből, amiket lapoknak nevezünk, állítsuk össze a nagy tömböt úgy, hogy felveszünk egy LT laptérkép tömböt, amelynek elemei lapokra mutató pointerek. Programozás Alapjai (2008)

Flexibilis tömb megvalósítása Ezt szemlélteti az ábra. ... L-1 ... L-1 LT L ... 2L-1 ... L-1 1 (N-1)/L ((N-1)/L)L ... N-1 N ... ... (N-1)%L ((N-1)%L)+1 ... L-1 Programozás Alapjai (2008)

Flexibilis tömb megvalósítása /* Globális elemek a flexibilis tömb megvalósításához */ #define L ??? /* lapméret */ typedef enum {false, true} bool; /* logikai típus */ typedef ??? elemtip; /* a tömb elemtípusa */ typedef elemtip *laptip; typedef laptip *lapterkeptip; typedef struct ftomb { lapterkeptip lt; /* laptérkép */ unsigned int hatar; /* aktuális indexhatár */ } ftomb; >>> Programozás Alapjai (2008)

Flexibilis tömb megvalósítása /* A műveletek megvalósítása: */ void kiolvas(ftomb a, unsigned int i, elemtip *x) { if(i < a.hatar) { *x = a.lt[i / L][i % L]; } void modosit(ftomb a, unsigned int i, elemtip x) a.lt[i / L][i % L] = x; >>> Programozás Alapjai (2008)

Flexibilis tömb megvalósítása void letesit(ftomb *a, unsigned int n) { int j; if(n) { a->hatar = n; a->lt=(elemtip**)malloc( (1+((n-1)/L))*sizeof(lapterkeptip)); for(j=0; j<=((n-1) / L); ++j) { a->lt[j]=(elemtip*)malloc(L*sizeof(elemtip)); /* lapok létesítése */ } } else { a->hatar = 0; a->lt = NULL; >>> Programozás Alapjai (2008)

Flexibilis tömb megvalósítása void megszuntet(ftomb *a) { int j; if(a->hatar) { for(j=0; j<=((a->hatar-1) / L); ++j) { free(a->lt[j]); /* lapok törlése */ } free(a->lt); a->hatar=0; unsigned int felso(ftomb a) return a.hatar; >>> Programozás Alapjai (2008)

Flexibilis tömb megvalósítása void novel(ftomb *a, unsigned int d) { int j; a->lt = (elemtip**)realloc(a->lt, (1+((a->hatar+d-1)/L))*sizeof(lapterkeptip)); for(j=(a->hatar ? ((a->hatar-1)/L)+1 : 0) ; j<=(a->hatar+d-1)/L; ++j) { a->lt[j]=(elemtip*)malloc(L*sizeof(elemtip)); } a->hatar += d; >>> Programozás Alapjai (2008)

Flexibilis tömb megvalósítása void csokkent(ftomb *a, unsigned int d) { int j; if(d <= a->hatar) { for(j=(a->hatar-d-1)/L +1; j<=(a->hatar-1)/L; ++j) { free(a->lt[j]); /* lapok törlése */ } a->hatar -= d; a->lt = (elemtip**)realloc(a->lt, (1+((a->hatar-1)/L))*sizeof(lapterkeptip)); Programozás Alapjai (2008)

Rendezés több szempont szerint Problémafelvetés A beolvasott adatokat rendezzük több szempont szerint is egy egyszerű rendezési algoritmussal és minden rendezés után legyen kiíratás is. Specifikáció Flexibilis tömbbel dolgozzunk Input A tömb elemei. Output A különböző szempontok szerint rendezett tömb. Programozás Alapjai (2008)

Rendezés több szempont szerint Algoritmustervezés: A fő algoritmusban csak az elemeket kell beolvasni egy végjelig, majd rendre aktivizálni kell a különböző szempontok szerinti rendezést, végül az eredményt kiíratni. A rendezés a beszúrórendezés lesz. Programozás Alapjai (2008)

Programozás Alapjai (2008) Beszúró rendezés Problémafelvetés Rendezzük egy tömb elemeit Specifikáció Input Egy tömb melynek elemtípusán értelmezett egy rendezési reláció. Output A reláció alapján rendezett tömb. Programozás Alapjai (2008)

Programozás Alapjai (2008) Beszúró rendezés Algoritmustervezés: A tömböt logikailag egy már rendezett és egy még rendezetlen részre osztjuk, és a rendezetlen rész első elemét beszúrjuk a rendezett elemek közé úgy, hogy azok rendezettek maradjanak. 3 1 5 4 2 Programozás Alapjai (2008)

Rendezés több szempont szerint /* Rendezzük névsorba illetve átlag szerint a hallgatókat! * Flexibilis tömbbel történik a megvalósítás, tehát a * névsor hosszát nem kell előre megmondani. * Készítette: Dévényi Károly, devenyi@inf.u-szeged.hu * 1998. Február 16. * Módosította: Gergely Tamás, gertom@inf.u-szeged.hu * 2006. Augusztus 15. */ #include <stdio.h> #include <string.h> #define L 10 /* lapméret */ >>> Programozás Alapjai (2008)

Rendezés több szempont szerint typedef enum {false, true} bool; /* logikai típus */ typedef struct elemtip { /* a tömb elemtípusa */ char nev[21]; float adat; } elemtip; typedef elemtip *laptip; typedef laptip *lapterkeptip; typedef struct ftomb { lapterkeptip lt; /* laptérkép */ unsigned int hatar; /* aktuális indexhatár */ } ftomb; typedef bool (*RendRelTip)(elemtip, elemtip); >>> Programozás Alapjai (2008)

Rendezés több szempont szerint /* A műveletek megvalósítása: */ void kiolvas(ftomb a, unsigned int i, elemtip *x) { if(i < a.hatar) { *x = a.lt[i / L][i % L]; } void modosit(ftomb a, unsigned int i, elemtip x) a.lt[i / L][i % L] = x; >>> Programozás Alapjai (2008)

Rendezés több szempont szerint void letesit(ftomb *a, unsigned int n) { int j; if(n) { a->hatar = n; a->lt=(elemtip**)malloc( (1+((n-1)/L))*sizeof(lapterkeptip)); for(j=0; j<=((n-1) / L); ++j) { a->lt[j]=(elemtip*)malloc(L*sizeof(elemtip)); /* lapok létesítése */ } } else { a->hatar = 0; a->lt = NULL; >>> Programozás Alapjai (2008)

Rendezés több szempont szerint void megszuntet(ftomb *a) { int j; if(a->hatar) { for(j=0; j<=((a->hatar-1) / L); ++j) { free(a->lt[j]); /* lapok törlése */ } free(a->lt); a->hatar=0; unsigned int felso(ftomb a) return a.hatar; >>> Programozás Alapjai (2008)

Rendezés több szempont szerint void novel(ftomb *a, unsigned int d) { int j; a->lt = (elemtip**)realloc(a->lt, (1+((a->hatar+d-1)/L))*sizeof(lapterkeptip)); for(j=(a->hatar ? ((a->hatar-1)/L)+1 : 0) ; j<=(a->hatar+d-1)/L; ++j) { a->lt[j]=(elemtip*)malloc(L*sizeof(elemtip)); } a->hatar += d; >>> Programozás Alapjai (2008)

Rendezés több szempont szerint void csokkent(ftomb *a, unsigned int d) { int j; if(d <= a->hatar) { for(j=(a->hatar-d-1)/L +1; j<=(a->hatar-1)/L; ++j) { free(a->lt[j]); /* lapok törlése */ } a->hatar -= d; a->lt = (elemtip**)realloc(a->lt, (1+((a->hatar-1)/L))*sizeof(lapterkeptip)); >>> Programozás Alapjai (2008)

Rendezés több szempont szerint void beszuroRend(ftomb t, RendRelTip kisebb) { /* A Kisebb rendezési reláció szerinti helyben rendezés */ int i,j; elemtip e,f; for(i = 1; i < felso(t); ++i) { kiolvas(t, i, &e); j = i-1; while(true) { if(j<0) break; kiolvas(t, j, &f); if(kisebb(f, e)) modosit(t, ((j--)+1), f); } modosit(t, j+1, e); } /* BeszuroRend */ >>> Programozás Alapjai (2008)

Rendezés több szempont szerint bool NevSzerint(elemtip X, elemtip Y) { /* a névsor szerinti rendezési reláció */ return strcmp(X.nev, Y.nev) <= 0; } bool AdatSzerint(elemtip X, elemtip Y) { /* az adat szerinti rendezési reláció */ return X.adat <= Y.adat; bool CsokAdatSzerint(elemtip X, elemtip Y) { /* az adat szerint csökkenő rendezési reláció */ return X.adat >= Y.adat; >>> Programozás Alapjai (2008)

Rendezés több szempont szerint int main() { ftomb sor; elemtip hallg; /* beolvasáshoz */ int i; letesit(&sor, 0); /* a flexibilis tömb létesítése */ /* beolvasás */ printf("Kérem az adatsort, külön sorban név és adat!\n"); printf("A végét a * jelzi.\n"); scanf("%20[^\n]%*[^\n]", hallg.nev); getchar(); i = 0; /* az i. helyre fogunk beírni */ while(strcmp(hallg.nev, "*")) { novel(&sor, 1); /* a flexibilis tömb bővítése */ scanf("%f%*[^\n]", &hallg.adat); getchar(); modosit(sor, i++, hallg); } >>> Programozás Alapjai (2008)

Rendezés több szempont szerint beszuroRend(sor, NevSzerint); /* Rend. névsor szerint */ printf("Névsor szerint rendezve:\n"); for(i = 0; i < felso(sor); ++i) { /* Kiíratás */ kiolvas(sor, i, &hallg); printf("%6.2f %s\n", hallg.adat, hallg.nev); } beszuroRend(sor, AdatSzerint); /* Rend. adat szerint */ printf("Adat szerint rendezve:\n"); beszuroRend(sor, CsokAdatSzerint); /* Rendezés újra */ printf("Adat szerint csökkenő sorba rendezve:\n"); megszuntet(&sor); /* a flexibilis tömb törlése */ Programozás Alapjai (2008)

Programozás Alapjai (2008) Láncok Tekintsük azonos típusú adatelemek sorozatát. Ha a sorozat bármely pozíciójára vonatkozó bővítés és törlés műveletet is akarunk végezni, akkor a tömbös reprezentálás nem lehet hatékony, ehelyett a sorozatnak láncolással történő reprezentálása ajánlott. Láncoláson olyan adatszerkezetet értünk, amely a tárolandó sorozat minden elemét egy olyan rekordban (cellában) tárolja, amely a sorozat következő elemének elérését biztosító információt is tartalmazza. Programozás Alapjai (2008)

Programozás Alapjai (2008) Láncok Az elérési információ lehet egy pointer érték, amely a sorozat következő elemét tartalmazó cellára (dinamikus változóra) mutató pointer. A sorozatot az első elemére mutató pointerrel adhatjuk meg. Sorozat 1. Adat 2. Adat 3. Adat 4. Adat Elérési információ Elérési információ Elérési információ NULL Programozás Alapjai (2008)

Programozás Alapjai (2008) Láncok C-ben így hozhatunk létre láncolt lista típust: typedef ??? elemtip; /* a sorozat elemtípusa */ typedef struct cellatip { elemtip adat; /* adatelem */ struct cellatip *csat; /* a következő elem cellájára */ } cellatip; typedef struct cellatip *pozicio; /* pointer */ Programozás Alapjai (2008)

Programozás Alapjai (2008) Láncok Deklaráljuk a p pointert! Lehet a következő módokon: cellatip *p; struct cellatip *p; pozicio p; A p pointer által megmutatott cella egyes mezőire így hivatkozhatunk: p->adat p->csat Programozás Alapjai (2008)

Programozás Alapjai (2008) Láncok A -> és . struktúraoperátorok a precedencia-hierarchia csúcsán állnak, és ezért nagyon szorosan kötnek. A ++p->adat nem p-t, hanem az adat mezőt inkrementálja, mivel az alapértelmezés szerinti zárójelezés: ++(p->adat) Programozás Alapjai (2008)

Programozás Alapjai (2008) Láncok Zárójelek használatával a kötés megváltoztatható: (++p)->adat az adat-hoz való hozzáférés előtt növeli p-t, míg (p++)->adat azt követően inkrementál. Láncok esetén vigyázzunk ezekkel a műveletekkel, mert egyáltalán nem biztos, hogy két cellatip típusú láncelem közvetlenül egymás után helyezkedik el a memóriában! Programozás Alapjai (2008)

Programozás Alapjai (2008) Láncok A lánc soron következő elemét a p->csat pointeren keresztül a *p->csat hozza be. Programozás Alapjai (2008)

Programozás Alapjai (2008) Láncok *p->csat++ azután inkrementálja csat-t, miután hozzáfért ahhoz, amire mutat (ekkor a lánc megszakadhat!) (*p->csat)++ azt növelné, amire csat mutat, (de ezt most nem lehet, mert ez egy struct) *p++->csat azután inkrementálja p-t, hogy hozzáfért ahhoz, amire csat mutat (de ekkor nem biztos, hogy p továbbra is a lánc valamelyik elemére mutat). Programozás Alapjai (2008)

Programozás Alapjai (2008) Láncok Láncok bejárására írhatunk olyan függvényt amelynek paramétere az elvégzendő művelet: typedef void (*muvelettip)(elemtip*); void bejar(pozicio lanc, muvelettip muv) { pozicio p; for(p = lanc; p != NULL; p = p->csat) { /* művelet a sorozat elemén */ muv(&(p->adat)); } Programozás Alapjai (2008)

Programozás Alapjai (2008) Típusokról C-ben A következőkben a C típusokhoz, típusképzésekhez, deklarációkhoz tartozó néhány tudnivalót tekintünk át. Programozás Alapjai (2008)

Programozás Alapjai (2008) Struct, Union, Enum Változódeklarációk: struct { int a, b; } vstruct; union { long long l; double d; } vunion; enum { nulla, egy, ketto, harom } venum; Programozás Alapjai (2008)

Programozás Alapjai (2008) Struct, Union, Enum A struct, union és enum a típusdeklaráció, típusdefiníció szempontjából hasonlóan működik, így amit a következőkben a struct típusról elmondunk, az analóg módon a union és enum típusokra is érvényes. Programozás Alapjai (2008)

Programozás Alapjai (2008) Struct, Union, Enum Ha ugyanilyen típusú változókat szeretnénk később is deklarálni akkor érdemes elnevezni a struktúrát: struct s { int a, b; } vstruct; ... struct s masodik; struct s harmadik; Programozás Alapjai (2008)

Programozás Alapjai (2008) Struct, Union, Enum De megtehetjük azt is, hogy egyszerűen csak definiáljuk a struktúrát (változódeklaráció nélkül), és később használjuk fel: struct s { int a, b; }; ... struct s masodik; struct s harmadik; Programozás Alapjai (2008)

Programozás Alapjai (2008) Struct, Union, Enum Sőt, megtehetjük azt is, hogy egyelőre csak deklaráljuk a struktúrát, és csak később definiáljuk (de a definíció NEM maradhat el!): struct s; ... struct s masodik; struct s harmadik; struct s { int a, b; } vstruct; Programozás Alapjai (2008)

Programozás Alapjai (2008) Struct, Union, Enum Változódeklaráció helyett készíthetünk típusdefiníciót is: typedef struct { int a, b; } S; ... S masodik; S harmadik; Programozás Alapjai (2008)

Programozás Alapjai (2008) Struct, Union, Enum Akár így is: struct s { int a, b; } vstruct; typedef struct s S; ... struct s masodik; S harmadik; s negyedik; /* ROSSZ !!! */ struct S otodik; /* ROSSZ !!! */ Programozás Alapjai (2008)

Programozás Alapjai (2008) Struct, Union, Enum Vagy így: typedef struct s { int a, b; } S; ... struct s masodik; S harmadik; s negyedik; /* ROSSZ !!! */ struct S otodik; /* ROSSZ !!! */ Programozás Alapjai (2008)

Programozás Alapjai (2008) Struct, Union, Enum De adhatjuk a struktúrának és a típusnak ugyanazt a nevet is: typedef struct st { int a, b; } st; ... struct st masodik; st harmadik; Programozás Alapjai (2008)

Bonyolultabb deklarációk A C nyelvben az összetett típusok megadásakor megismert műveleteket véges sokszor ismételhetjük. Mivel a deklaráció nem olvasható egyszerűen balról jobbra és mivel a * művelet alacsonyabb prioritású a [] és () műveleteknél, ezért érdemes néhány példával megvilágítani a bonyolultabb eseteket. Programozás Alapjai (2008)

Bonyolultabb deklarációk A prioritási előírás csökkenő sorrendben B elemkiválasztások és fgv. ( [], ., ->, () ) J a egyoperandusú műveletek ( -, ++, --, !, ~, &, *, sizeof ) B a multiplikatív műveletek ( *, /, % ) B az additív műveletek ( +, - ) B bitléptetés ( <<, >> ) B a kisebb-nagyobb relációs műveletek ( <=, >=, <, > ) B az egyenlő-nem egyenlő relációs műveletek ( ==, != ) B bitenkénti 'és' művelet ( & ) B bitenkénti 'kizáró vagy' művelet ( ^ ) B bitenkénti 'vagy' művelet ( | ) B a logikai 'és' művelet ( && ) B a logikai 'vagy' művelet ( || ) J a feltételes művelet ( ? : ) J értékadó művelet ( =, +=, -=, *=, /=, %=, >>=, <<=, &=, ^=, |= ) B szekvencia művelet ( , ) Programozás Alapjai (2008)

Bonyolultabb deklarációk Bár a prioritási előírást a műveletek elvégzésénél vezettük be, de ugyanezt az előírást használjuk a deklarációnál is. A műveletek elvégzésének sorrendjét zárójelek használatával befolyásolhatjuk. Ezt tehetjük a deklarációknál is. Pl: int *p /* int-re mutató pointer */ int x[10] /* tömb 10 int-ből */ int (*x)[10] /* pointer mutat egy tömbre, ami 10 int-ből áll */ int *x[10] /* tömb 10 pointerből, melyek int-re mutatnak */ Programozás Alapjai (2008)

Bonyolultabb deklarációk void (*f)(int) /* pointer egy függvényre, aminek paramétere int és nincs visszaadott értéke */ int (*f)(void) /* pointer egy függvényre, aminek nincs paramétere és a visszaadott érték típusa int */ int (*x[])(int) /* tömb, amelyik pointerekből áll. Ezek olyan függvényre mutatnak, melynek paramétere is és visszaadott értékének típusa is int. */ Programozás Alapjai (2008)

Bonyolultabb deklarációk Lehet-e ennél bonyolultabbakat is? char (*(*f())[])() függvény visszatér pointerrel, ami egy tömbre mutat és pointerekből áll, amelyek olyan függvényre mutatnak, aminek visszaadott értékének típusa char char (*(*x[3])())[5] 3 pointerből álló tömb. A pointerek olyan függvényre mutatnak, amely pointerrel tér vissza. Ezek 5 char-ból álló tömbre mutatnak. Programozás Alapjai (2008)

Bonyolultabb deklarációk Mit lehet tanácsolni, ha bonyolult deklarációt kell elkészíteni? Például hogyan kell deklarálni egy N elemű tömböt, amely függvényre mutató pointerekből áll. A függvény olyan pointerrel tér vissza, amely függvényre mutat, és a függvény karakterre mutató pointerrel tér vissza. Három módszert szoktak ajánlani: Próbáljuk meg felírni: char *(*(*a[N])())(); Programozás Alapjai (2008)

Bonyolultabb deklarációk Típusdefiníciót használva építsük fel lépésenként a deklarációt. A megjegyzésekben a megfelelő angol terminologiát is bevezetjük: typedef char *pc; /* pointer to char */ typedef pc fpc(); /* function returning pointer to char */ typedef fpc *pfpc; /* pointer to ... */ typedef pfpc fpfpc(); /* function returning ... */ typedef fpfpc *pfpfpc; /* pointer to ... */ pfpfpc a[N]; /* array of ... */ Programozás Alapjai (2008)

Bonyolultabb deklarációk Használjuk a cdecl programot, amelyik angolról C-re és megfordítva képes deklarálni illetve a deklarációt elmagyarázni. cdecl> declare a as array of pointer to function returning pointer to function returning pointer to char char *(*(*a[])())() cdecl> explain char *(*(*a[])())() declare a as array of pointer to function returning pointer to function returning pointer to char Programozás Alapjai (2008)

Bonyolultabb deklarációk És ez még mind semmi, mert a függvények paramétereiről nem mondtunk semmit! A paraméterek típusa ugyanis megadható és minden paraméternek a típusa az előzőekhez hasonló bonyolultságú lehet. Azt is szokták tanácsolni, hogy egy bonyolult deklaráció megértését kezdjük bentről kifelé. Programozás Alapjai (2008)

Bonyolultabb deklarációk A C nyelvben nemcsak arra van szükség, hogy egy azonosítót változóazonosítóvá vagy típusazonosítóvá deklaráljunk, hanem arra is, hogy magát a típust adjuk meg. Ezt kell tenni függvénydeklarátoroknál a paraméterek típusának megadásakor, sizeof argumentumaként, típuskényszerítésnél. A típus megadása szintaktikailag ugyanúgy történik, mint a változó deklarálása, csak az azonosító marad el. Programozás Alapjai (2008)

Bonyolultabb deklarációk int /* egész */ int * /* int-re mutató pointer */ int [10] /* tömb 10 int-ből */ int (*)[10] /* pointer mutat egy tömbre, ami 10 int-ből áll */ int *[10] /* tömb 10 pointerből, melyek int-re mutatnak */ void (*)(int) /* pointer egy függvényre, melynek paramétere int és nincs visszaadott értéke */ int (*)(void) /* pointer egy függvényre, melynek nincs paramétere és a visszaadott érték típusa int */ int (*[])(int) /* tömb, amelyik pointerekből áll. Ezek olyan függvényre mutatnak, melynek paramétere is és visszaadott értékének típusa is int. */ Programozás Alapjai (2008)

Programozás Alapjai (2008) Típuskényszerítés A típuskényszerítés egy konverziós művelet, amelyet egy ()-be zárt típusmegadással írunk elő. A prioritási előírás csökkenő sorrendben B elemkiválasztások és fgv. ( [], ., ->, () ) J a egyoperandusú műveletek ( -, ++, --, !, ~, &, *, sizeof ) J típuskényszerítés ( () ) B a multiplikatív műveletek ( *, /, % ) B az additív műveletek ( +, - ) B bitléptetés ( <<, >> ) B a kisebb-nagyobb relációs műveletek ( <=, >=, <, > ) B az egyenlő-nem egyenlő relációs műveletek ( ==, != ) B bitenkénti 'és' művelet ( & ) B bitenkénti 'kizáró vagy' művelet ( ^ ) B bitenkénti 'vagy' művelet ( | ) B a logikai 'és' művelet ( && ) B a logikai 'vagy' művelet ( || ) J a feltételes művelet ( ? : ) J értékadó művelet ( =, +=, -=, *=, /=, %=, >>=, <<=, &=, ^=, |= ) B szekvencia művelet ( , ) Programozás Alapjai (2008)

Programozás Alapjai (2008) Típuskényszerítés Példa típuskényszerítésre: Két int tipusú változó osztása egészosztás és int eredményt ad. Ha nem egészosztást szeretnénk, akkor kényszerítsük az egyik operandust valamelyik valós típusúvá: int i = 7; j = 2; double x; x = i / j; /* x értéke 3.0 lesz */ x = (double) i / j; /* x értéke 3.5 lesz */ Programozás Alapjai (2008)

Programozás Alapjai (2008) Típuskényszerítés Példa típuskényszerítésre: A dinamikus változók számára leggyakrabban a malloc() függvénnyel foglalunk helyet. A függvény a malloc.h-ban van deklarálva: typedef unsigned size_t; void *malloc(size_t); void free(void *); void *realloc(void *, size_t); void *calloc(size_t, size_t); Programozás Alapjai (2008)

Programozás Alapjai (2008) Típuskényszerítés A függvény lefoglalja a megadott bájtszámú területet és a címét visszadja. Ha nem sikerül a tárterület lefoglalása, akkor a visszaadott érték NULL. A malloc() függvény alkalmazásánál típuskényszerítést kell használni. Pl. int *p; p = (int *) malloc(100 * sizeof(int)); Programozás Alapjai (2008)

Programozás Alapjai (2008) Típuskényszerítés Hasonlóan kell eljárni bonyolultabb típusok esetén: typedef struct cellatip { Elemtip adat; /* adatelem */ struct cellatip *csat; /* a következő elem cellájára */ } cellatip; typedef struct cellatip *pozicio; /* pointer típus */ pozicio p; p = (cellatip *) malloc(sizeof(cellatip)); Programozás Alapjai (2008)

Programozás Alapjai (2008) Típuskényszerítés A típuskényszerítés egy konverziós művelet, tehát nem alkalmazható mindig. Pl. Van egy void * p pointer. Tudom, hogy most int-re mutat. Szeretném a pointert léptetni. Lehet-e így? ((int *)p)++; Így nem lehet, de így már igen: p = (void *)((int *)p + 1); vagy egyszerűen így p += sizeof(int); Programozás Alapjai (2008)

Programozás Alapjai (2008) Az I/O alapjai A be- és kiviteli (I/O) szolgáltatások nem részei a C nyelvnek. Az ANSI szabvány szerint standard könyvtár áll rendelkezésre. Ha használni akarjuk, akkor a programban szerepelnie kell a következő sornak: #include <stdio.h> Programozás Alapjai (2008)

Programozás Alapjai (2008) Az I/O alapjai C-ben megkülönböztetünk Alacsony szintű Magas szintű fájlkezelést. Magas szintű fájlkezelés esetén külön beszélünk az úgynevezett standard fájlok kezeléséről. Programozás Alapjai (2008)

Magas szintű fájlkezelés A hordozhatóság érekében ajánlott ezt használni. Az adatokat egy adatfolyamnak (stream) tekintjük. A stream I/O pufferezett, vagyis az írás/olvasás fizikailag nagyobb darabokban történik. A puffer hossza a stdio.h-ban van definiálva: #define BUFSIZ _IO_BUFSIZ ahol #define _G_BUFSIZ 8192 #define _IO_BUFSIZ _G_BUFSIZ Programozás Alapjai (2008)

Magas szintű fájlkezelés A UNIX-ban 3 előre definiált stream van: stdin stdout stderr Mindhárom karakterfolyamnak tekinthető és a program indulásakor automatikusan úgy nyitódnak meg, hogy az stdin a billentyűzethez az stdout és a stderr a képernyőhöz rendelődik. Programozás Alapjai (2008)

Standard fájlok kezelése Az stdin, stdout, stderr magas szinten, szöveges módban kezelt fájlok A fájlkezelő függvények a stdio.h-ban vannak Programozás Alapjai (2008)

Magas szintű fájlkezelés Természetesen a UNIX-ban szokásos átírányításokkal az eredmény adatállományba is írható, illetve az input adatok adatállományból is vehetők, de ez nem a C, hanem az operációs rendszer tulajdonsága. Sok függvény áll rendelkezésünkre, hogy egy karakterfolyamot kezeljünk. Programozás Alapjai (2008)

Programozás Alapjai (2008) Alapvető függvények int getchar(void) Makró, mely beolvas egy karaktert a stdin-ről. Az EOF konstans értéket adja vissza, amikor az általa éppen olvasott, bármiféle bemenet végére ért (például lenyomtuk a <ctrl> + d billantyűkombinációt Linux-ban) Implementációtól függően, de általában pufferelten olvas: A program csak akkor kapja meg a begépelt karakter(eke)t, ha a puffer betelt vagy sorvéget (fájlvégét) nyomtunk. Közvetlen billentyűleütés érzékelésére nem alkalmas, de nem is ez a feladata. (A közvetlen billentyűleütést csak oprendszerfüggő módon tudnánk figyelni.) Programozás Alapjai (2008)

Programozás Alapjai (2008) Alapvető függvények char *gets(char *buf) Beolvas egy sort a stdin-ről a buf-ba. A buf egy létező és elegendő hosszú karaktertömb kell, hogy legyen, mert: Nem ellenőrzi, hogy szabad vagy foglalt memóriaterületet ír-e felül. Válaszértéke A karaktertömb címe sikeres olvasáskor . NULL pointer sikertelen olvasáskor (hiba vagy fájlvége a sor elején). Programozás Alapjai (2008)

Programozás Alapjai (2008) Alapvető függvények A sor közbeni fájlvége esetén Lezárja a sztringet a buf-ban. A buf pointerrel tér vissza. Az újsor karaktert nem olvassa be, hanem '\0' karaktert tesz helyette a buf-ba (eltér az fgets() függvénytől!), ezért nem alkalmas az újsor karakter beolvasására (arra használhatjuk a getchar() makrót). Egy megjegyzés a gets manual oldalról: Never use gets! Soha ne használd a gets függvényt! A buffer overflow súlyos biztonsági hiba lehet! Programozás Alapjai (2008)

Programozás Alapjai (2008) Alapvető függvények int putchar(int ch) Makró, mely kiír egy karaktert a stdout-ra. A kiírt karakter kódját adja vissza sikeres művelet esetén, EOF-ot ha hiba történt. Nem pufferelt. Programozás Alapjai (2008)

Programozás Alapjai (2008) Alapvető függvények int puts(const char *buf) A buf stringet kiírja a stdout-ra. A sztringvége karakter helyett újsort ír. Válaszértéke: Egy nemnegatív érték sikeres végrehajtás esetén. EOF hiba esetén. Programozás Alapjai (2008)

Programozás Alapjai (2008) Példa A bemenet kisbetűssé alakítása: #include <stdio.h> #include <ctype.h> main() { int c; while ((c = getchar()) != EOF) { putchar(isupper(c) ? tolower(c) : c); /* putchar(tolower(c)); is jó megoldás */ } Programozás Alapjai (2008)

Formatált I/O műveletek int printf(char *formátum, ...) Kinyomtatja a stdout-ra az aktuális paraméterek értékeit a formátum szövegben előírt formában. A visszatérési érték a kiírt karakterek száma. A formázó karakterlánc kétféle típusú objektumot tartalmaz: Közönséges karaktereket, amelyeket egyszerűen a kimeneti folyamra másol. Konverzió-specifikációkat, amelyek mindegyike a soron következő paraméter konvertálását és kiíratását írja elő. Programozás Alapjai (2008)

Formatált I/O műveletek Formátumbeli speciális ESCAPE karakterek: \a hangjelzés \n újsor \r CR \b backspace \t vízszintes tabulátor \v függőleges tabulátor \f lapdobás (form-feed) Programozás Alapjai (2008)

Formatált I/O műveletek Formátumbeli speciális ESCAPE karakterek: \" ” \\ \ \NNN Az NNN oktális kódú karakter (legfeljebb 3 darab). \xnn Az nn hexadecimális kódú karakter. \unnnn Az nnnn hexadecimális kódú 16 bites Unicode karakter. \Unnnnnnnn Az nnnnnnnn hexadecimális 32 bites Unicode kódú karakter. Programozás Alapjai (2008)

Formatált I/O műveletek Egy konverzió-specifikáció (más néven formátumvezérlő szekvencia) általános alakja: %[flag][n[.m]][{h,l}]type (A [] közötti rész szabadon elhagyható.) Programozás Alapjai (2008)

Formatált I/O műveletek %[flag][n[.m]][{h,l}]type A type egy karakter, melyet konverziós karakternek nevezünk. A konverziós karakterek és jelentésük: d,i A paraméter decimális jelölésmódúvá alakul (int, short, long, long long). u A paraméter előjel nélküli decimális jelölésmódúvá alakul (unsigned int, unsigned short, unsigned long, unsigned long long). Programozás Alapjai (2008)

Formatált I/O műveletek A paraméter előjel nélküli oktális számmá konvertálódik (unsigned int, unsigned short, unsigned long, unsigned long long). x,X A paraméter előjel nélküli hexadecimális számmá konvertálódik (unsigned int, unsigned short, unsigned long, unsigned long long). c A paraméter egyetlen karakter (char). Programozás Alapjai (2008)

Formatált I/O műveletek A paramétert float-nak vagy double-nak tekinti,és a [-]mmm.nnnnnn decimális jelölésmódba konvertálja, ahol az n-ek karakterláncának hosszát a pontosság adja meg. Az alapértelmezés szerinti pontosság 6. e,E A paramétert float-nak vagy double-nak tekinti, és a [-]m.nnnnnnE[-]xx decimális jelölésmódba konvertálja, ahol a számjegyek számát a pontosság adja meg. Az alapértelmezés szerinti pontosság 6. g,G %e és %f közül a rövidebbet használja; az értéktelen nullákat elhagyja. Programozás Alapjai (2008)

Formatált I/O műveletek s A paraméter karakterlánc (sztringre mutató pointer); a láncbeli karakterek a pontossággal megadott darabszámig vagy a nulla karakterig mindaddig nyomtatódnak. p Cím (pointer) hexadecimális formában. n Az eddig a pontig kiírt karakterek számát adja vissza a megfelelő paraméter által mutatott int változóban. % Kiírja a % karaktert. Ha a %-ot követő karakter nem konverziós karakter, az illető karakter nyomtatásra kerül. Programozás Alapjai (2008)

Formatált I/O műveletek %[flag][n[.m]][length]type A flag hiányában a kiíratás jobbra igazítva, balról szóközökkel kitöltve történik - balra igazítás. + előjel kötelező kiírása. ’ ’ (szóköz) kötelező előjel, + helyett szóközt ír. 0 számok előtt a kitöltés a '0' karakterrel megy. # Lebegőpontosnál kötelező tizedespont. o,x,X esetén 0, 0x, 0X kiírása nullától különböző értékek előtt. Programozás Alapjai (2008)

Formatált I/O műveletek %[flag][n[.m]][length]type Az n a mezőszélességet jelenti Egy decimális szám, amely a minimális mezőszélességet határozza meg. (A számérték nem kezdődhet 0-val, mert azt flag-ként értelmezi.) Ha a szám helyén egy '*' szerepel, akkor a szélességet a következő argumentum kifejezés értéke határozza meg: printf("%*d",5,125); printf("%5d", 125); Programozás Alapjai (2008)

Formatált I/O műveletek A következő argumentum értéke legalább ilyen széles, vagy szükség esetén szélesebb mezőbe nyomtatódik ki. Ha a konvertált argumentum kevesebb karakterből áll, mint a mezőszélesség, akkor bal oldalon (vagy, ha a balra igazítás jelző szerepel, akkor jobb oldalon) a mező kitöltődik, hogy ezáltal az előírt mezőszélesség meglegyen. A kitöltő karakter közönséges esetben szóköz, ill. amennyiben a számot íratunk ki jobbra igazítva, és megadtuk a 0 flag-et, akkor a '0' karakter. Programozás Alapjai (2008)

Formatált I/O műveletek %[flag][n[.m]][length]type Az m a pontosságot jelenti Decimális szám: i, d, o, x, X, u esetén legalább ennyi számjegyet ír ki, szükség esetén balról a '0' karakterrel kitöltve a helyet. f, e, E, g, G esetén tizedesjegyek illetve értékes jegyek száma; az utolsó jegyet kerekíti. Ha 0, akkor a tizedespont sem kerül kiíratásra. Hiányában az alapértelmezés 6. s esetén a sztringből kiírható karakterek maximális száma. Ha a szám helyén egy '*' szerepel, akkor a pontosságot a következő argumentum kifejezés értéke határozza meg. Programozás Alapjai (2008)

Formatált I/O műveletek %[flag][n[.m]][length]type A length a hosszmódosító hh Az argumentumot char-ként kezeli. h Az argumentumot short-ként kezeli. l, L Az argumentumot long-ként illetve long double-ként kezeli. ll Az argumentumot long long-ként kezeli. Programozás Alapjai (2008)

Formatált I/O műveletek Figyeljük meg, hogy A printf() kerekít A konvertálás csonkít #include <stdio.h> int main() { int i; double d; for(d=-2.0; d<=2.0; d+=1.0/3.0) { i=d; printf("d=%23.20lf; %5.2lf; d=%2.0lf; i=%2d;\n", d, d, d, i); } Programozás Alapjai (2008)

Formatált I/O műveletek d=-2.00000000000000000000; -2.00; d=-2; i=-2; d=-1.66666666666666674068; -1.67; d=-2; i=-1; d=-1.33333333333333348136; -1.33; d=-1; i=-1; d=-1.00000000000000022204; -1.00; d=-1; i=-1; d=-0.66666666666666696273; -0.67; d=-1; i= 0; d=-0.33333333333333364790; -0.33; d=-0; i= 0; d=-0.00000000000000033307; -0.00; d=-0; i= 0; d= 0.33333333333333298176; 0.33; d= 0; i= 0; d= 0.66666666666666629659; 0.67; d= 1; i= 0; d= 0.99999999999999955591; 1.00; d= 1; i= 0; d= 1.33333333333333281523; 1.33; d= 1; i= 1; d= 1.66666666666666607455; 1.67; d= 2; i= 1; d= 1.99999999999999933387; 2.00; d= 2; i= 1; Programozás Alapjai (2008)

Formatált I/O műveletek Mi történik a printf() hívásakor ? Pl.: printf("%d %d %d\n", 10, 20) A verembe (stack) beletevődnek az argumentumok, de milyen sorrendben? Lényeges, hogy kiolvasásnál a formátum sztring megelőzze a többi argumentumot, hiszen abból tudjuk, hogy hány és milyen típusú értékeket kell kiolvasnunk. Programozás Alapjai (2008)

Formatált I/O műveletek Tehát a formátum sztringnek kell a verem „tetején” lennie, ezért utolsónak kell betenni. Tehát a paramétereket a C jobbról balra haladva helyezi el a veremben, egyre csökkenő címeken, végül még beteszi a visszatérési címet Programozás Alapjai (2008)

Formatált I/O műveletek printf("%d %d %d\n", 10, 20); ... 20 10 "%d %d %d\n" címe Visszatérési cím Programozás Alapjai (2008)

Formatált I/O műveletek A függvényhívás után az argumentumokat a veremből törölni kell; de ki takarít? Ha a hívott printf() visszatérés előtt takarítana, akkor a fenti esetben a formátum sztring alapján még egy további (nem ide tartozó) értéket kitörölne a veremből és az egész összezavarodna. Tehát az argumentumokat a hívó függvény törli a visszatérés után, hisz ő tudja biztosan, hogy ténylegesen hány paramétert adott át. Programozás Alapjai (2008)

Formatált I/O műveletek A programozó felelőssége, hogy adott konverzió-specifikációhoz megfelelő típusú argumentumérték tartozzon: A program kimenete: #include <stdio.h> int main() { printf("%20lld %20lld\n", (long long)2005, 2005.0); printf("%20f %20f\n", (long long)2005, 2005.0); } 2005 4656532898701115392 0.000000 2005.000000 Programozás Alapjai (2008)

Formatált I/O műveletek Vagy: A program kimenete: #include <stdio.h> int main() { printf("%x %x %x %x\n", 1, 2, 3, 4); printf("%x %x %x %x\n", (long long)1, (long long)2, (long long)3, (long long)4); printf("%llx %llx %llx %llx\n", printf("%llx %llx %llx %llx\n", 1, 2, 3, 4); } 1 2 3 4 1 0 2 0 200000001 400000003 bffd8d78bffd8df4 b7fabff40804844a Programozás Alapjai (2008)

Formatált I/O műveletek printf("%x %x\n", (long long)1, (long long)2); 00 00 00 00 00 00 00 00 00 2 00 00 00 00 00 02 02 00 00 00 00 00 00 00 00 00 1 00 00 00 00 00 01 01 ?? Formátumsztring címe ?? ?? ?? ?? ?? ?? ?? ?? Visszatérési cím ?? ?? ?? ?? ?? ?? ?? Programozás Alapjai (2008)

Formatált I/O műveletek printf("%llx %llx\n", 1, 2); ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 00 2 00 00 00 00 00 02 02 00 1 00 00 00 00 00 01 01 ?? Formátumsztring címe ?? ?? ?? ?? ?? ?? ?? ?? Visszatérési cím ?? ?? ?? ?? ?? ?? ?? Programozás Alapjai (2008)

Formatált I/O műveletek int scanf(char *formátum, ...) Beolvassa a stdin-ről a karaktereket. Konvertálja a megfelelő formátumra. Elhelyezi a paraméterlistában megadott memóriacímekre. A visszatérési érték a beolvasott és sikeresen eltárolt értékek száma. A formátumvezérlés technikája megegyezik a printf() formátumvezérlésével. Programozás Alapjai (2008)

Formatált I/O műveletek A formátum string fix karaktereit sorra összeveti a stdin-ről olvasottakkal. A láthatatlan karaktereket egyetlen szóköznek tekinti. A formátumparaméterek helyén megpróbálja elvégezni a konverziót. Megkeresi az első még fel nem használt (azaz következő) argumentumot és arra a címre elhelyezi az eredményt. Programozás Alapjai (2008)

Formatált I/O műveletek Például: Az stdin: 3 eredmény: 3 4 5<ENTER> Akkor v1=3 v2=3 int v1, v2; scanf("%d eredmény: %d", &v1, &v2); Programozás Alapjai (2008)

Formatált I/O műveletek A formátum általános alakja: %[*][width][{h,l}]type * Az adatmezőt beolvassa, de nem helyezi el sehova (tehát ehhez nem kell változócímet megadni) Programozás Alapjai (2008)

Formatált I/O műveletek %[*][width][length]type width (szélesség) A konverzió során figyelembe veendő karakterek száma; hiányában addig olvas, míg logikai alapon meg nem találja a megfelelő mező végét (pl. a típushoz nem illő karaktert). Programozás Alapjai (2008)

Formatált I/O műveletek %[*][width][length]type length hh A paraméter char * h A paraméter short int* l A paraméter long int* vagy double* L A paraméter long long* vagy long double* Programozás Alapjai (2008)

Formatált I/O műveletek %[*][width][length]type type c width darab (ha nincs megadva, akkor 1) karakter (char*) d,D decimális szám (char*, ... int*) o,O oktális szám (char*, ... int*) x,X hexadecimális szám (char*, ... int*) Programozás Alapjai (2008)

Formatált I/O műveletek i,I (char*, ... int*) 0-val kezdődő szám oktális 0x-el kezdődő szám hexadecimális 0-tól különböző egésszel kezdődő decimális szám u,U előjeltelen decimális érték (unsigned char*, unsigned ... int*) f lebegőpontos érték (float*,double*,long double*) Programozás Alapjai (2008)

Formatált I/O műveletek s Karakterlánc (sztring) (char*) Hosszúsága legfeljebb width, az első whitespace karakterig olvas, melyet sztringvégi null karakterrel helyettesít. [karakterek] Sztring kizárólag a megadott karakterekből (char*) Hosszúsága: width, vagy az első olyan karakterig, amit nem soroltunk fel. [^karakterek] Sztring kizárólag a meg NEM adott karakterekből (char*) Hosszúsága: width, vagy az első olyan karakterig, amit felsoroltunk. Programozás Alapjai (2008)

Formatált I/O műveletek Problémák: Ha egy karaktert nem tud értelmezni, akkor abbahagyja az olvasást. A maradékot benne hagyja az stdin-ben. A program nem tudja, hogy mi volt a probléma. Üres sztringet a %s segítségével nem tudunk beolvasni. %[^\n] esetén az újsor karaktert már nem dolgozza fel. Ha az újsort betesszük a formátum sztringbe, akkor beolvassa a scanf, de a beolvasás végét egy újabb sorvéggel kell jeleznünk. Programozás Alapjai (2008)

Formatált I/O műveletek Megtehetjük, hogy minden scanf után getchar()-okkal ellépkedünk az újsor utánig, de lehet, hogy akkor értékes karaktereken is túlmegyünk. Másik megoldás: scanf("%d%*[^\n]", &X); getchar(); Programozás Alapjai (2008)

Programozás Alapjai (2008) Alternatív I/O int sscanf(char *mp, char *form,…) A memória adott területét megpróbálja a scanf()-hez hasonlóan értelmezni és átmásolni. A scanf() kiváltására használhatunk sscanf()-et is, az legalább nem az stdin fájlmutatóját rontja el, hanem csak egy memóriapointert. int sprintf(char *mp, char *form,…) A printf() adott memóriaterületre író változata, belső konverziók elvégzésére is használható. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz A már említett 3 fájl, az stdin, stdout és stderr úgy képzelhető el, mint egy folyam: Mi csak nyitni és zárni tudjuk a „zsilipet”, látjuk mi folyik át, de visszahozni azt már nem tudjuk. Vagy másképpen: mi csak egy irányban (előre) lépegethetünk a fájlban lévő elemek sorozatán, mindig csak a következő elemre. Általánosságban viszont egy fájl tartalma megmarad, tehát akárhányszor újra ránézhetünk tettszőleges részére. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz Minden fájl felfogható elemek (jelen esetben karakterek) sorozataként. Ehhez a sorozathoz tartozik egy író-olvasó fej, ami a fájl értékét jelentő sorozatot két részsorozatra bontja. Tehát egy fájl minden lehetséges értéke [a0 ... ai-1][ai ... an-1] alakban adható meg. Az író-olvasó fej által kijelölt elem a második sorozat első eleme. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz Bármilyen művelet a fájlon (egy elem írása vagy olvasása) csak a fejen keresztül történhet. Lehetséges elemi műveletek: Olvasás: kiolvassuk a fej által kijelölt elem értékét és a fej egyet továbblép. Írás: megváltoztatjuk a fej által kijelölt elem értékét, vagy ha az a fájl vége volt, akkor hozzáfűzzük az új elemet; a fej egyet továbblép. Pozícionálás: a fejet tudjuk mozgatni. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz Minden fájlhoz egy stdio.h-beli FILE struktúrát rendelünk hozzá. A FILE struktúra sokféle mezőt tartalmaz (fej pozíció, puffer, stb.), ezekkel azonban nem kell (és nem is érdemes) direkt módon dolgoznunk. E helyett függvényeket használunk, amikben a fájlhoz rendelt struktúra címe fogja a fájlt azonosítani. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz Ha tehát fájlokkal szeretnénk dolgozni, a következő műveletekre lesz szükségünk: Először a FILE típusú változóhoz hozzárendelünk egy operációs rendszerbeli adatállományt vagy eszközt: FILE *fopen(const char *path, const char *mode); Majd használjuk. Végül lezárjuk: int fclose(FILE *fp); Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz FILE *fopen(const char *path, const char *mode) A path az operációs rendszerben létező fájlra való szabályos hivatkozás. Ha a megnyitás nem sikerült, akkor a visszatérési érték NULL Ha sikerült, akkor egy FILE-re mutató pointer. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz A mode a megnyitás módja, ami lehet: "r" (read): Egy már létező fájl megnyitása olvasásra. Ha nem létezik vagy nincs rá olvasási jogunk, az hiba. "w" (write): Egy fájl megnyitása írásra. Ha már létezett, a régi tartalma törlődik, egyébként létre lesz hozva. Ha nincs rá írásjogunk, vagy nem tudjuk létrehozni, az hiba. "a" (append): Egy fájl megnyitása hozzáfűzésre. Ha már létezett, a régi tartalma megmarad, az új a végéhez fűződik. Ha nem létezett a fájl, akkor egyenértékű a "w"-vel. Ha nincs rá írásjogunk, vagy nem tudjuk létrehozni, az hiba. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz "r+" (read+write): Egy már létező fájl megnyitása olvasásra és írásra. Ha nem létezik vagy nincs rá jogunk, az hiba. "w+" (write+read): Egy fájl megnyitása írásra és olvasásra. Ha már létezett, a régi tartalma törlődik, egyébként létre lesz hozva. Ha nincs rá jogunk, vagy nem tudjuk létrehozni, az hiba. "a+" (append+read): Egy fájl megnyitása hozzáfűzésre és olvasásra. Ha már létezett, a régi tartalma megmarad, az új a végéhez fűződik. Ha nem létezett a fájl, akkor egyenértékű a "w"-vel. Ha nincs rá jogunk, vagy nem tudjuk létrehozni, az hiba. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz A mode bizonyos implementációkban tartalmazhatja még: 't' (text): Szöveges mód. 'b' (binary): Bináris mód. E két módnak linux alatt nincs jelentőssége. Ha a megjelölés elmarad, a text mód az alapértelmezés. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz Az író-olvasó fej r, r+, w, w+ esetén a fájl elejére, a, a+ esetén a fájl végére áll. Ha a fájlt r+, w+, a+ módok valamelyikében nyitottuk meg és váltani akarunk írás és olvasás között, akkor célszerű a puffert az fseek() vagy fflush() függvények valamelyikével a kiüríttetni. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz Az fopen használata: A zárójelezésre nagyon kell vigyázni! Biztonságosabb, ha az értékadás egy külön sorban történik meg: FILE *fp; /* fájlpointer */ if ((fp = fopen("/etc/motd", "r")) == NULL) { printf("Nem sikerült megnyitni a /etc/motd adatállományt"); exit(1); } fp = fopen("/etc/motd", "r"); if (fp == NULL) { ... } Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz Alapvető műveletek: int fgetc(FILE *fp) Egy karakter olvasása fp-ről. int getc(FILE *fp) Egy karakter olvasása fp-ről (makró). int fputc(char ch, FILE *fp) Egy karakter írása fp-re. int putc(char ch, FILE *fp) Egy karakter írása fp-re (makró). Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz int fseek(FILE *fp, long mennyi, int honnan) Fájlok direkt elérése, vagyis a fej pozícionálása. Lépteti a fájlpointert mennyi bájtnyit. honnan (viszonyítva) lehet: SEEK_SET : a fájl elejétől. SEEK_CUR : jelenlegi fájlpozíciótól. SEEK_END : fájl végétől. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz A fájlpointer értéke negatív nem lehet, de mutathat a fájlvégén túlra is, ekkor a rendszer addig a pozícióig nyújtja (szeméttel kiegészíti) a fájlt, és akkor is ott fogja a műveleteket végezni. Ha a pozicionálás sikeres volt, akkor 0-t ad vissza. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz char *fgets(char *buf, int n, FILE *fp) Egy sort (de legfeljebb n-1 karaktert) olvas be a fájlból, beleértve a sorvég karaktert is, ezt elhelyezi a buf karaktertömbbe, amelyet sztringvégi nulla karakterrel lezár. Visszatérési értéke: A tömb címe (valóban haszontalan, hisz ezt mi adtuk meg paraméterként) – ha sikerült a művelet. NULL – hiba esetén (fájl vége, olvasási hiba). Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz int fputs(char *buf, FILE *fp) A buf sztring tartalmát kiírja a fájlba. Nem tesz a sor végére automatikusan újsort. Válaszértéke: Az utolsó kiírt karakter kódja . 0 – ha a string üres volt. EOF – hiba esetén. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz int fprintf(FILE *fp, char *form,…) A printf() függvény fájlba író változata. int fscanf(FILE *fp, char *form,…) A scanf() függvény fájlból olvasó változata. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz int ungetc(int c, FILE *fp) Visszateszi a c karaktert a fájlra úgy, hogy a legközelebbi olvasás ezt a karaktert fogja olvasni. Felhasználás: pl. számjegyeket olvasunk be, amikor nem számjegy karakter jött, azt visszatesszük, a számjegyeket pedig egy számmá konvertáljuk. Legfeljebb egy karaktert lehet visszatenni, az ungetc() többszöri egymás utáni meghívása nem megengedett. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz long ftell(FILE *fp) Lekérdezi a fej pozícióját. int feof(FILE *fp) Válaszérték: 0: még nem értük el a fájl végét. más: elértük a fájl végét. int ferror(FILE *fp) 0: a fájl megnyitása óta nem történt hiba. más: hiba történt. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz int fread(char *buf, int rsize, int rn, FILE *fp) Az rsize hosszúságú csomagokból rn darabot olvas be a buf címre. Visszaadja a ténylegesen beolvasott csomagok számát. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz int fwrite(char *buf, int rsize, int rn, FILE *fp) Az rsize hosszúságú csomagokból rn darabot ír ki a buf címről. Visszaadja a ténylegesen kiírt csomagok számát. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz int fflush(FILE *fp) Kiírja a puffer tartalmát. Válaszértéke: 0: sikeres EOF: sikertelen int fclose(FILE *fp) Lezárja a fájlt. Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz Természetesen az előre definiált 3 streamet is kezelhetjük ezekkel a függvényekkel, pl: fprintf(stderr, "Nem működik a gépem"); fputs(stdout, "Sztring\n"); fscanf(stdin, "%s", nev); fgets(stdin, 50, sor); Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz #include <stdio.h> main(){ FILE *fp, *fpb; int a,b; fp=fopen("probat.txt","wt"); fprintf(fp,"%d",34); fflush(fp); fclose(fp); fp = fopen("probat.txt","rt"); fscanf(fp,"%d",&a); printf("%d\n",a); fclose(fp); Programozás Alapjai (2008)

Hozzáférés az adatállományokhoz fpb =fopen("probab.dat","w+b"); fwrite(&a,sizeof(int),1,fpb); fseek(fpb, 0, SEEK_SET); fread(&b,sizeof(int),1,fpb); printf("%d\n",b); fclose(fpb); } Az output: 34 Programozás Alapjai (2008)

A C forrás fordításának folyamata A fordítási folyamat sok lépésből is állhat, de 4 olyan lépés van, ahol a folyamatot elkezdeni illetve befejezni lehet. preprocessing – előfeldolgozás compilation – fordítás (assembly nyelvre) assembly – fordítás (gépi kódra) linking – szerkesztés Programozás Alapjai (2008)

A C forrás fordításának folyamata A fájl végződése utal a programozási nyelvre és arra, hogy mit kell vele csinálni: preprocesszor C fordító assembler linker .c .i .s .o a.out .h p.c gcc -E p.c >p.i p.i gcc -S p.i p.s gcc -c p.s p.o gcc p.o a.out as p.s ld p.o ... gcc -S p.c gcc p.s gcc -c p.c gcc p.i gcc -c p.i gcc p.c Programozás Alapjai (2008)

Programozás Alapjai (2008) A C előfeldolgozó A C előfeldolgozó (preprocesszor) egy makroprocesszor, melyet a C fordítóprogram automatikusan meghív, mielőtt a tényleges fordításhoz hozzákezdene. Azért nevezzük makroprocesszornak, mert lehetőségünk van úgynevezett makrók definiálására, hogy a hosszú konstrukciókat lerövidíthessük. Programozás Alapjai (2008)

Programozás Alapjai (2008) A C előfeldolgozó A C előfeldolgozó 4 egymástól független lehetőséget kinál: Fájlok (általában header fájlok) bemásolása. Makrobővítés. (Macro expansion) Feltételes fordítás. (Conditional compilation) Sor vezérlés. (Line control) Programozás Alapjai (2008)

Programozás Alapjai (2008) A C előfeldolgozó A C előfeldolgozó nincs tisztában a C nyelv szintaxisával, csupán egy sororientált szövegszerkesztésről van szó. Mindig elvégzi a következő egyszerű műveleteket: Minden kommentet kicserél egy darab szóközre. (A /* és */ közötti részeket.) \újsor karakterkombinációt (azaz sorvégi \-t) törli. Az előre definiált makrókat behelyettesíti. Programozás Alapjai (2008)

Programozás Alapjai (2008) A C előfeldolgozó Az előfeldolgozó számára szóló utasítások #-kal kezdődnek. Nem szükséges az első pozícióra írni, de a # kell legyen az első értelmes karakter a sorban. Programozás Alapjai (2008)

Programozás Alapjai (2008) Header fájlok A header fájlok használata kettős: A rendszer header fájlokban az operációs rendszerrel és a szabványos könyvtárakkal való kapcsolatot definiálják. #include <fájl> Ha nem találja a fájl-t a standard (előre definiált) helyen, akkor keresi a -I fordítási opcióval megadott könyvtárban. Programozás Alapjai (2008)

Programozás Alapjai (2008) Header fájlok A saját header fájlokban azok a deklarációk szerepelnek, amiket több forrásprogramunkban is fel szeretnénk használni. #include "fájl" Ha nem találja a fájl-t az aktuális könyvtárban, akkor keresi a -I fordítási opcióval megadott könyvtárban. Programozás Alapjai (2008)

Programozás Alapjai (2008) Header fájlok Ha az előfeldolgozó talál egy neki szóló #include utasítást, akkor a megadott fájlt egyszerűen sorról sorra bemásolja az adott helyre, és a bemásolt rész elejéről folytatja a munkát. Programozás Alapjai (2008)

Programozás Alapjai (2008) Makrók Az egyszerű makró egy rövidítés, amit bevezetünk és a későbbiekben használunk. A C-ben a konstansokat például így definiáljuk: Mindenütt, ahol mostantól kezdve a TOMB_MERET megjelenik, az előfeldolgozó helyettesíti ezt az 1020-al, tehát a C fordító már a sok 1020 értéket fogja látni. #define TOMB_MERET 1020 Programozás Alapjai (2008)

Programozás Alapjai (2008) Makrók Az egyszerű makrók értékeit be tudjuk állítani a fordító -D opciójával is. Az egyszerű makrónál mindig ugyanazt a szöveget helyettesíti az előfeldolgozó. Programozás Alapjai (2008)

Programozás Alapjai (2008) Paraméteres makrók Ha a makrónak paraméterei is vannak, akkor a függvényhíváshoz hasonlóan más-más paraméterekkel is meghívhatjuk, így a helyettesítés is más és más lehet. Ha ezek után a programban szerepel a szöveg, akkor az előfeldolgozó behelyettesíti erre: #define min(X,Y) ((X)<(Y)?(X):(Y)) min(a,b) ((a)<(b)?(a):(b)) Programozás Alapjai (2008)

Előre definiált makrók A standard előre definiált makrókat az ANSI szabvány rögzíti. Nem standard előre definiált makrók pl. unix minden UNIX rendszerben m88k Motorola 88000 processzornál sparc Sparc processzornál sun minden SUN számítógépen svr4 System V Release 4 szabványú UNIX operációs rendszerben A makró definiciót hatástalanítani lehet az #undef-fel. Programozás Alapjai (2008)

Programozás Alapjai (2008) Feltételes fordítás Itt is az if alapszó szolgál az egyszerű szelekció megvalósítására. Míg a C programban szereplő if utasítás a program végrehajtása közben érvényesül, addig az előfeldolgozónál használt #if még a fordítás előtt értékelődik ki és ennek megfelelően más és más forráskód kerül lefordításra. Programozás Alapjai (2008)

Programozás Alapjai (2008) Feltételes fordítás Miért használunk feltételes fordítást? A géptől és az operációs rendszertől függően más-más forráskódra van szükségünk. Ugyanazt a programot több célból is használni szeretnénk, pl. van benne nyomkövetés, míg a végső változatban már nincs. Programozás Alapjai (2008)

Programozás Alapjai (2008) Feltételes fordítás A feltételes fordítás direktivái: #if, #ifdef, #ifndef Egyszerű szelekció: Egyszerű szelekció egyébként ággal: #if kifejezés ... #endif /* kifejezés */ #if kifejezés ... /* text-if-true */ #else ... /* text-if-false */ #endif Programozás Alapjai (2008)

Programozás Alapjai (2008) Feltételes fordítás Például: vagy: esetleg #if defined(DEBUG) printf("%d\n", v); #endif #ifdef(DEBUG) printf("%d\n", v); #endif #if DEBUG > 2 printf("%d\n", v); #endif Programozás Alapjai (2008)

Programozás Alapjai (2008) Feltételes fordítás Többszörös szelekció (egyébként ággal) #if kifejezes1 ... /* első ág */ #elif kifejezes2 ... /* második ág */ #elif kifejezes3 ... /* harmadik ág */ #else ... /* negyedik ág */ #endif Programozás Alapjai (2008)

Programozás Alapjai (2008) Sor vezérlés A C forráskód több helyekről másolodik össze. A #line direktivával megadhatjuk, hogy ez legyen most a forrássor sorszáma a könnyebb beazonosíthatóság végett. #line sorszám Programozás Alapjai (2008)

Programozás Alapjai (2008) Sor vezérlés $ cat preproc.c #define N 30 #ifdef DEBUG #define STRING "Debug" #else #define STRING "Release" #endif #line 200 main() { int unix; char tomb[N] = STRING; for(unix=N-1; unix && tomb[unix]; unix--) { tomb[unix] = 0; } Programozás Alapjai (2008)

Programozás Alapjai (2008) Sor vezérlés $ gcc -E preproc.c # 1 "preproc.c" # 1 "<built-in>" # 1 "<command line>" # 200 "preproc.c" main() { int 1; char tomb[30] = "Release"; for(1=30 -1; 1 && tomb[1]; 1 --) { tomb[1] = 0; } Programozás Alapjai (2008)

Programozás Alapjai (2008) Sor vezérlés $ gcc -DDEBUG -E preproc.c # 1 "preproc.c" # 1 "<built-in>" # 1 "<command line>" # 200 "preproc.c" main() { int 1; char tomb[30] = "Debug"; for(1=30 -1; 1 && tomb[1]; 1 --) { tomb[1] = 0; } Programozás Alapjai (2008)

Programozás Alapjai (2008) Sor vezérlés $ gcc preproc.c preproc.c: In function 'main': preproc.c:202: error: syntax error before numeric constant preproc.c:204: error: invalid lvalue in assignment preproc.c:204: error: invalid lvalue in decrement Programozás Alapjai (2008)

A C forrás fordításának folyamata A fájl végződése utal a programozási nyelvre és arra, hogy mit kell vele csinálni: preprocesszor C fordító assembler linker .c .i .s .o a.out .h p.c gcc -E p.c >p.i p.i gcc -S p.i p.s gcc -c p.s p.o gcc p.o a.out as p.s ld p.o ... gcc -S p.c gcc p.s gcc -c p.c gcc p.i gcc -c p.i gcc p.c Programozás Alapjai (2008)

Programozás Alapjai (2008) A C fordító A C fordító tehát tulajdonképpen már egy preprocesszált fájlt kap, ami tisztán C nyelvi elemekből áll, és tartalmaz minden deklarációt ahhoz, hogy önmagában le lehessen fordítani. A C fordító nem csak „szó szerint” tudja lefordítani a C programot, hanem különféle optimalizálások elvégzésére is képes. Programozás Alapjai (2008)

Programozás Alapjai (2008) A C fordító A gcc fordítónak a –O kapcsolóval tudjuk megmondani, hogy milyen optimalizálásokat alkalmazzon: -O0: nincs optimalizálás A C forrás minden egyes művelete le lesz fordítva assemblyre, még akkor is, ha az nyilvánvalóan felesleges. Ez általában nagyon nagy és lassú gépi kódot eredményez, viszont hibakeresésnél igen hasznos, hiszen minden egyes gépi kódú utasításról egyértelműen megmondható, hogy az eredetileg melyik C-beli művelethez tartozott. Programozás Alapjai (2008)

Programozás Alapjai (2008) A C fordító -O1: optimalizálás A C fordító elvégzi azokat az optimalizálásokat amik mind a méretet, mind a futási időt csökkentik. -O2: optimalizálás futási időre A C fordító az O1 –ből indulva elvégzi azokat az optimalizálásokat amik csökkentik a futási időt, de a méreten (O1-hez képest) csak keveset növelnek. -O3: agresszív optimalizálás futásidőre A C fordító elvégzi azokat az futásidőre optimalizáló algoritmusokat is, amik jelentősen növelhetik a kód méretét. Programozás Alapjai (2008)

Programozás Alapjai (2008) A C fordító -Os: optimalizálás méretre A C fordító az O1 –ből indulva elvégzi azokat az optimalizálásokat amik csökkentik a kód méretét, tekintet nélkül a futási időre gyakorolt hatásuktól. A fordítás gcc esetében nem közvetlenül gépi kódra, hanem assembly nyelvre történik. Programozás Alapjai (2008)

Alapszavak a C nyelvben Ismétlésképpen foglaljuk össze az alapszavakat: auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while Programozás Alapjai (2008)

Alapszavak a C nyelvben char Karakter típus, 1 bájt. double Valós típus, 8 bájt. float Valós típus, 4 bájt. int Egész típus, 32 bites rendszereken 4 bájt. Programozás Alapjai (2008)

Alapszavak a C nyelvben long „hosszú”: long int (4), long long int (8), long double (8) short „rövid”: short int (2) signed „előjeles”: int, char unsigned „előjeltelen”: int, char Programozás Alapjai (2008)

Alapszavak a C nyelvben enum Felsorolás típus képzése. struct Szorzat-rekord típus képzése. union Egyesített-rekord típus képzése. Programozás Alapjai (2008)

Alapszavak a C nyelvben typedef Típusképzés. void Az „üres” típus. sizeof Típus méretének lekérdezése. Programozás Alapjai (2008)

Alapszavak a C nyelvben auto Automatikus tárolási osztály: A globálisan deklarált változóknak „statikus” helyet foglal a program teljes futási idejére. A blokkokban lokálisan deklarált változóknak a veremben foglal helyet az adott blokk végrehajtásának idejére. static Statikus tárolási osztály: Az ilyen változónak mindenképpen „statikus”, vagyis állandó helyet foglal a program teljes futási idejére. Programozás Alapjai (2008)

Alapszavak a C nyelvben register Regiszter tárolási osztály: A fordító megpróbálja mindenképpen regiszterben tartani a változót (ez nem biztos, hogy sikerül). Ez gyorsabb elérést biztosít, cserében a változónak nem lesz címe. extern Külső tárolási osztály: A fordító az ilyen változóknak itt nem foglal helyet, mert ez valahol máshol megtörténik, itt csak azt jelezzük, hogy van ilyen változó. A változóhivatkozások feloldása majd a linker feladata lesz. Programozás Alapjai (2008)

Alapszavak a C nyelvben const Ha konstansnak deklarálunk egy változót, akkor a fordítóprogram nem engedi, hogy (az inicializáláson kívül, ami ilyenkor kötelező) értéket adjunk neki. A változó értéke kerülővel azért módosítható. volatile Ez a kulcsszó azt jelzi, hogy a változó értékét más is módosíthatja. Ennek az eredménye az, hogy a fordító minden egyes műveletnél használni fogja a változóhoz rendelt memóriaterületet, azaz erre a változóra nem végez optimalizálást. Programozás Alapjai (2008)

Alapszavak a C nyelvben if Az egyszerű szelekciós vezérlés kulcsszava. else A szelekciós vezérlés egyébként ágának kulcsszava. Programozás Alapjai (2008)

Alapszavak a C nyelvben switch Az esetkiválasztásos szelekciós vezérlés kulcsszava. case Az esetkiválasztásos szelekciós vezérlés blokkjában egy belépési pont kulcsszava. default Az esetkiválasztásos szelekciós vezérlés blokkjában az egyébként fel nem sorolt esetek belépési pontja. Programozás Alapjai (2008)

Alapszavak a C nyelvben for A ciklusszervezés egyik kulcsszava, általában számlálásos ismétlésekhez. while A ciklusszervezés másik kulcsszava, általában kezdő- vagy végfeltételes ismétlésekhez. do A végfeltételes ciklus kezdetét jelző kulcsszó. Programozás Alapjai (2008)

Alapszavak a C nyelvben break Kiugrás az adott ciklusból vagy switch utasításból, az azt követő első utasításra. continue A ciklusmag aktuális végrehajtásának befejezése, ugrás az inkrementálásra vagy a feltételkiértékelésre. goto A feltétel nélküli vezérlésátadás megvalósításának kulcsszava. (Ne használjuk.) Programozás Alapjai (2008)

Alapszavak a C nyelvben return Visszatérés a függvényből a hívás helyére, és a visszatérési érték megadása. Programozás Alapjai (2008)

A C forrás fordításának folyamata A fájl végződése utal a programozási nyelvre és arra, hogy mit kell vele csinálni: preprocesszor C fordító assembler linker .c .i .s .o a.out .h p.c gcc -E p.c >p.i p.i gcc -S p.i p.s gcc -c p.s p.o gcc p.o a.out as p.s ld p.o ... gcc -S p.c gcc p.s gcc -c p.c gcc p.i gcc -c p.i gcc p.c Programozás Alapjai (2008)

Programozás Alapjai (2008) Az assembler Az assembly kódot fogja gépi kóddá fordítani, és egy úgynevezett object fájlt fog létrehozni. Egy ilyen object bináris formában tartalmazza az assembly kódból keletkezett gépi kódot, a programban található konstans értékeket (például a programban leírt számokat vagy sztringeket), illetve sok olyan technikai információt, amire szükség lesz ahhoz, hogy az object-et össze lehessen szerkeszteni más objectekkel (pl. szimbólumtábla). Programozás Alapjai (2008)

Programozás Alapjai (2008) Fordítási egységek Mint látható, eddig a pontig minden C forrásnak „egyenes” az útja: a header fájlokat nem számolva egy C forrásból egy object keletkezik. Eddig a pontig kivétel nélkül bármelyik helyes C forrás eljuttatható. Futtatható program azonban csak olyan forrásból készíthető, amelyben van main() függvény. Programozás Alapjai (2008)

A C forrás fordításának folyamata A fájl végződése utal a programozási nyelvre és arra, hogy mit kell vele csinálni: preprocesszor C fordító assembler linker .c .i .s .o a.out .h p.c gcc -E p.c >p.i p.i gcc -S p.i p.s gcc -c p.s p.o gcc p.o a.out as p.s ld p.o ... gcc -S p.c gcc p.s gcc -c p.c gcc p.i gcc -c p.i gcc p.c Programozás Alapjai (2008)

Programozás Alapjai (2008) A linker Első megközelítésben a linker olyan object fájlból tud futtatható programot csinálni, amiben van main() függvény. De akkor miért kell ehhez linker? Mire jó a többi object (amiben nincs main)? Ahhoz, hogy ezt megértsük, meg kell ismerkednünk a modulok fogalmával. Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok A korszerű programozási nyelvek lehetővé teszik programok mellett programrendszerek nyelvi szinten történő kezelését is. A programrendszer nem más, mint modulok hierarchiája. A modul olyan programozási egység, amely program elemek (konstans, típus, változó, függvény) együtteséből áll. Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok Egy modulban szereplő program elemek egy része felhasználható más modulok (programok) által, ezek képezik a modul közösségi, vagy látható részét, míg a modul többi része kívülről nem látható, ez a modul privát része. A modulnak e két részre osztása biztosítja a felhasznált modulok stabilitását és a privát elemek védelmét. Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok A modulok természetes egységei a fordításnak, tehát minden modul külön fordítható. (Fontos hangsúlyozni, hogy a külön fordítás nem független fordítást jelent.) Következésképpen a modulok két formában léteznek: Forrásnyelvi forma. Lefordított forma. Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok C-ben C-ben alapvető egységként adódik az egy forrás/object, mint legkisebb lehetséges modul. A .o kiterjesztésű object fájl ekkor a modul „lefordított formája” lesz, a .c kiterjesztésű forrás pedig a modul privát részének nyelvi formája. A modul közösségi részét pedig a már említett .h kiterjesztésű header fájlok valósítják meg. Programozás Alapjai (2008)

Programozás Alapjai (2008) Header fájlok A header és forrás fájlok tehát általában párosan léteznek: A header fájl tartalmazza az összes olyan változó és függvény deklarációját, illetve konstans és típus definícióját, ami a modul közösségi részét képezi. Ha valaki használni akarja a modult, csak include-olnia kell ezt a header fájlt, és máris használhatja az ebben deklarált dolgokat. A forrás pedig tartalmazza a közösségi részben deklarált függvények definícióit, illetve a modul teljes privát részét. Programozás Alapjai (2008)

Programozás Alapjai (2008) Header fájlok Ahhoz tehát, hogy olyan programot vagy másik modult írjunk, ami használja a modulunkat, elegendő a modulunk header fájlját ismerni. Az adott program forrásába a megfelelő #include helyére a preprocesszor bemásolja a mi header fájlunkat, így az object szintig lefordítható lesz. Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok fordítása Egy-egy programot vagy modult le tudunk fordítani object szintig, függetlenül attól, hogy milyen más modulokat használ. A main() nélküli objectekkel többet már nem nagyon tehetünk, maximum össze tudjuk őket gyűjteni egy úgynevezett függvénykönyvtárban ami valójában egy olyan fájl, ami objecteket tartalmaz. A main() függvénnyel rendelkező objectekből viszont programok készíthetők, és ez a linker feladata. Programozás Alapjai (2008)

Programozás Alapjai (2008) A Linker Egy-egy program object-je önmagában eléggé hiányos lehet, hiszen ha egy modul valamelyik függvényét használja, annak csak a deklarációját ismeri. A függvény megvalósítása a modul object fájljában van. A linker feladata, hogy az ilyen objecteken átívelő hivatkozásokat feloldja, és egyetlen programot állítson elő sok .o fájlból. A sok .o közül pontosan egynek tartalmaznia kell a main() függvényt, hiszen az operációs rendszer majd ezt „hívja meg” a program indításakor. Programozás Alapjai (2008)

A C forrás fordításának folyamata preprocesszor C fordító assembler archiver m1.h m1.c m1.i m1.s m1.o m.a m2.h linker m2.c m2.i m2.s m2.o p.c p.i p.s p.o a.out Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok Látható tehát, hogy egy-egy modul több programhoz is felhasználható, gondoljunk csak például a math.h-ra. Az ebben deklarált függvények (pl.: sin()) egy függvénykönyvtárban vannak megvalósítva. A –lm kapcsoló mondja meg a linkernek, hogy a libm.a fájlban is keresgéljen függvények után. Meg kell jegyeznünk, hogy a modul kifejezés általában nem csupán egy .o fájlt, hanem egy vagy akár több függvénykönyvtárat is takarhat. Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok C-ben Hogyan szokás tehát C-ben modulokat írni? Mivel a C nyelv nem engedi például típusok újradeklarálását, viszont bonyolultabb programrendszerekben gyakran előfordul, hogy ugyanazt a header fájlt (tranzitívan) többször is includolnák ugyanabba a forrásba, ezért szokás ezt feltételes preprocesszor direktívákkal kivédeni. Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok C-ben p.c #include <stdio.h> #include "m.h" int main(int argc, char *argv[]) { printf("%d\n", fuggveny(argv[0])); } Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok C-ben m.h #ifndef M_H #define M_H int fuggveny(const char*); #endif Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok C-ben m.c #include "m.h" int fuggveny(const char *str) { int cs; for(cs=0; *str; str++) { cs^=*str; } return cs; Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok C-ben p.i ... # 2 "p.c" 2 # 1 "m.h" 1 int fuggveny(const char*); # 3 "p.c" 2 int main(int argc, char *argv[]) { printf("%d\n", fuggveny(argv[0])); } Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok C-ben m.i ... int fuggveny(const char*); # 2 "m.c" 2 int fuggveny(const char *str) { int cs; for(cs=0; *str; str++) { cs^=*str; } return cs; Programozás Alapjai (2008)

Programozás Alapjai (2008) Modulok C-ben A prímszámok keresését például megírhatjuk úgy, hogy a halmaz kezelését egy külön forrásban valósítjuk meg. Programozás Alapjai (2008)

Programozás Alapjai (2008) halmaz.h /* Header fájl a halmaz modulhoz. A logikai típust most a * preprocesszor segítségével definiáljuk. * 2006. Augusztus 16. Gergely Tamás, gertom@inf.u-szeged.hu */ #ifndef HALMAZ_H #define HALMAZ_H #ifndef BOOL #define BOOL int #define FALSE 0 #define TRUE 1 #endif #define K (8*sizeof(KisHalmaz)) typedef int KisHalmaz; typedef unsigned long int halmazelem; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) halmaz.h typedef struct halmaztip { unsigned long int n; /* Az univerzum a [0..N) */ unsigned long int m; /* A kishalmazok száma */ KisHalmaz *tar; /* A kishalmazok tömbje */ } Halmaz; Halmaz Letesit(halmazelem); void Megszuntet(Halmaz); void Uresit(Halmaz); void Bovit(Halmaz, halmazelem); void Torol(Halmaz, halmazelem); BOOL Eleme(Halmaz, halmazelem); void Egyesites(Halmaz H1, Halmaz H2, Halmaz H); void Metszet(Halmaz H1, Halmaz H2, Halmaz H); void Kulonbseg(Halmaz H1, Halmaz H2, Halmaz H); BOOL Egyenlo(Halmaz H1, Halmaz H2); BOOL Resz(Halmaz H1, Halmaz H2); BOOL Urese(Halmaz H); void Ertekadas(Halmaz H1, Halmaz H2); #endif Programozás Alapjai (2008)

Programozás Alapjai (2008) halmaz.c /* A halmaz modul függvényeinek megvalósítása. * 2006. Augusztus 16. Gergely Tamás, gertom@inf.u-szeged.hu */ #include <stdlib.h> #include "halmaz.h" >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) halmaz.c Halmaz Letesit(unsigned long int n) { Halmaz ret; ret.n = n; if(n) { ret.m = (n-1)/K + 1; ret.tar = (KisHalmaz*)malloc(ret.m * sizeof(KisHalmaz)); } else { ret.m = 0; ret.tar = NULL; } return ret; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) halmaz.c void Megszuntet(Halmaz H) { free(H.tar); } void Uresit(Halmaz H) long int i; for(i = 0; i < H.m; ++i) { H.tar[i] = 0; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) halmaz.c void Bovit(Halmaz H, halmazelem x) { if(x < H.n) { H.tar[x / K] |= (1 << (x % K)); } void Torol(Halmaz H, halmazelem x) H.tar[x / K] &= ~(1 << (x % K)); >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) halmaz.c BOOL Eleme(Halmaz H, halmazelem x) { return (x < H.n) && (H.tar[x / K] & (1 << (x % K))); } void Egyesites(Halmaz H1, Halmaz H2, Halmaz H) long int i; for(i = 0; i < H.m; ++i) { H.tar[i] = H1.tar[i] | H2.tar[i]; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) halmaz.c void Metszet(Halmaz H1, Halmaz H2, Halmaz H) { long int i; for(i = 0; i < H.m; ++i) { H.tar[i] = H1.tar[i] & H2.tar[i]; } void Kulonbseg(Halmaz H1, Halmaz H2, Halmaz H) H.tar[i] = H1.tar[i] & ~(H2.tar[i]); >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) halmaz.c BOOL Egyenlo(Halmaz H1, Halmaz H2) { long int i; for(i=0; (i<H1.m) && (H1.tar[i] == H2.tar[i]); ++i); return i == H1.m; } BOOL Resz(Halmaz H1, Halmaz H2) for(i = 0; (i<H1.m) && !(H1.tar[i] & ~(H2.tar[i])); ++i); >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) halmaz.c BOOL Urese(Halmaz H) { long int i; for(i = 0; (i<H.m) && !(H.tar[i]); ++i); return i == H.m; } void Ertekadas(Halmaz H1, Halmaz H2) for(i = 0; i < H1.m; ++i) { H1.tar[i] = H2.tar[i]; Programozás Alapjai (2008)

Programozás Alapjai (2008) prim3.c /* * Határozzuk meg az adott N természetes számnál nem nagyobb * prímszámokat. * Készítette: Dévényi Károly, devenyi@inf.u-szeged.hu * 1997. December 6. * Módosította: Gergely Tamás, gertom@inf.u-szeged.hu * 2006. Augusztus 16. */ #include <stdio.h> #include "halmaz.h" #define N 100000 >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) prim3.c int main() { Halmaz szita; halmazelem p, s; long int lepes, i, j; szita=Letesit(N); Bovit(szita, 2); /* a szita inicializálása */ for(i = 3; i <= N; i+=2) { Bovit(szita, i); } >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) prim3.c p = 3; /* az első szitálandó prím */ while(p*p <= N) { /* P többszöröseinek kiszitálása */ lepes = 2 * p; /* lépésköz = 2*p */ s = p*p; /* s az első többszörös */ while(s <= N) { Torol(szita, s); s += lepes; } do { /* a következő prím keresése */ p += 2; } while((p < N) && (! Eleme(szita, p))); >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) prim3.c j = 0; /* a prímszámok kiíratása képernyőre */ printf("A prímszámok %ld-ig:\n", N); for(p = 2; p < N; ++p) { if(Eleme(szita, p)) { printf("%8d", p); if(++j==10) { putchar('\n'); j=0; } Megszuntet(szita); Programozás Alapjai (2008)

Megvalósítás elrejtése Mire jók még a modulok? Programrendszer készítésekor az egyes modulokat célszerű úgy tervezni, hogy a headerben ne legyenek láthatók azok a programelemek, amelyek csak a megvalósításhoz kellenek. Programozás Alapjai (2008)

Megvalósítás elrejtése Ennek két oka is van: Egyrészt ezzel biztosítani tudjuk, hogy a modult felhasználó csak azokhoz a programelemekhez tud hozzáférni, amit a műveletek szabályos használata megenged, ezzel védettséget biztosítunk a lokális elemek számára. Másrészt a megvalósításhoz használt elemek elrejtése az implementációs részbe azt eredményezi, hogy a megvalósítás módosítása, megváltoztatása esetén nem kell a programrendszer más modulját változtatni, pl. újrafordítani. Programozás Alapjai (2008)

Megvalósítás elrejtése Egyedül az adattípusok elrejtése okozhat problémát. Ugyanis azoknak az eljárásoknak a deklarációjában, amelyek a probléma megoldását adják – tehát a headerben kell lenniük – meg kell adni a paraméterek típusát, jóllehet a típus definiálását csak a modul .c forrásában kellene megadni, mert ezek megadása már a megvalósításhoz tartozik. Programozás Alapjai (2008)

Megvalósítás elrejtése Adattípus megadásának elhalasztása, vagyis elrejtése a void* pointer felhasználásával megoldható. Ezt mutatja a következő séma, amelyben az mm.h az m.h t adattípusának elrejtését mutatja. Természetesen az mm.h -ban kell lennie olyan eljárásnak, amely t típusú dinamikus változót létesít. Programozás Alapjai (2008)

Megvalósítás elrejtése m.h m.c typedef d t; void fgv(t*); void fgv(t *x) { ... (*x) } Programozás Alapjai (2008)

Megvalósítás elrejtése mm.h mm.c typedef void *t; void fgv(t); typedef d *tt; void fgv(t x) { tt xx=x; ... (*xx) } Programozás Alapjai (2008)

Megvalósítás elrejtése Az elrejtés technikája lehetővé teszi, hogy modul tervezésekor meg tudjuk adni a header rész végleges alakját, anélkül, hogy a megvalósítás bármely részletét is ismernénk. Ez különösen hasznos absztrakt adattípusok tervezése és megvalósítása esetén. A függvénykönyvtárakból ráadásul csak azok az eljárások kerülnek bele a programba, amiket valóban meg is hívunk. Programozás Alapjai (2008)

Programozás Alapjai (2008) graf.h /* * Gráf megvalósítása típuselrejtéssel. Közös header fájl. * 2006. Augusztus 17. Gergely Tamás, gertom.inf.u-szeged.hu */ #ifndef GRAF_H #define GRAF_H /* Típusdefiníciók */ typedef void *graf; typedef int pont; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf.h graf letesit(int); int pontokszama(graf); void elbeszur(graf, pont, pont); void eltorol(graf, pont, pont); int vanel(graf, pont, pont); int kifok(graf, pont); int kielek(graf, pont, pont[]); int befok(graf, pont); int beelek(graf, pont, pont[]); void megszuntet(graf); #endif Programozás Alapjai (2008)

Programozás Alapjai (2008) graf1.c /* * Gráf megvalósítása típuselrejtéssel. * 2006. Augusztus 17. Gergely Tamás, gertom.inf.u-szeged.hu */ #include <stdlib.h> #include "graf.h" typedef struct _graft { int n; char *mx; } _graft; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf1.c graf letesit(int n) { _graft *ptr; ptr=(_graft*)malloc(sizeof(_graft)); if(ptr) { ptr->n =n; ptr->mx=(char*)malloc(n*n*sizeof(char)); if(!ptr->mx) { free(ptr); ptr=NULL; } else { int i, nn; for(i = 0, nn = n * n; i < nn; i++) { ptr->mx[i] = 0; } return (void*)ptr; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf1.c int pontokszama(graf g) { return ((_graft*)g)->n; } void elbeszur(graf g, pont f, pont t) if(0 <= f && f < ((_graft*)g)->n && 0 <= t && t < ((_graft*)g)->n) { ((_graft*)g)->mx[((_graft*)g)->n * f + t]=1; void eltorol(graf g, pont f, pont t) ((_graft*)g)->mx[((_graft*)g)->n * f + t]=0; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf1.c int vanel(graf g, pont f, pont t) { return 0 <= f && f < ((_graft*)g)->n && 0 <= t && t < ((_graft*)g)->n && ((_graft*)g)->mx[((_graft*)g)->n * f + t]; } int kifok(graf g, pont p) int i,fok=-1; if(0 <= p && p < ((_graft*)g)->n ) { for(fok=i=0; i<((_graft*)g)->n; i++) { fok+=((_graft*)g)->mx[((_graft*)g)->n * p + i]; return fok; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf1.c int kielek(graf g, pont p, pont l[]) { int i,fok=-1; if(0 <= p && p < ((_graft*)g)->n ) { for(fok=i=0; i<((_graft*)g)->n; i++) { if(((_graft*)g)->mx[((_graft*)g)->n * p + i]) { l[fok++]=i; } return fok; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf1.c int befok(graf g, pont p) { int i,fok=-1; if(0 <= p && p < ((_graft*)g)->n) { for(fok=i=0; i<((_graft*)g)->n; i++) { fok+=((_graft*)g)->mx[((_graft*)g)->n * i + p]; } return fok; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf1.c int beelek(graf g, pont p, pont l[]) { int i,fok=-1; if(0 <= p && p < ((_graft*)g)->n ) { for(fok=i=0; i<((_graft*)g)->n; i++) { if(((_graft*)g)->mx[((_graft*)g)->n * i + p]) { l[fok++]=i; } return fok; void megszuntet(graf g) if(g) { free(((_graft*)g)->mx); } free(g); Programozás Alapjai (2008)

Programozás Alapjai (2008) graf2.c /* * Gráf megvalósítása típuselrejtéssel. * 2006. Augusztus 17. Gergely Tamás, gertom.inf.u-szeged.hu */ #include <stdlib.h> #include "graf.h" typedef struct _graft { int n; int *be; int *ki; pont *mx; } _graft; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf2.c graf letesit(int n) { _graft *ptr; ptr=(_graft*)malloc(sizeof(_graft)); if(ptr) { ptr->n =n; ptr->be=(int*)malloc(n*sizeof(int)); ptr->ki=(int*)malloc(n*sizeof(int)); ptr->mx=(pont*)malloc(n*n*sizeof(pont)); if(!(ptr->mx && ptr->be && ptr->ki)) { free(ptr->mx); free(ptr->be); free(ptr->ki); free(ptr); ptr=NULL; } else { int i; for(i = 0; i < n; i++) { ptr->be[i] = 0; ptr->ki[i] = 0; } return (void*)ptr; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf2.c int pontokszama(graf g) { return ((_graft*)g)->n; } void elbeszur(graf g, pont f, pont t) if(0 <= f && f < ((_graft*)g)->n && 0 <= t && t < ((_graft*)g)->n && !vanel(g, f, t)) { ((_graft*)g)->mx[ ((_graft*)g)->n * f + ((_graft*)g)->ki[f]++ ] = t; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf2.c void eltorol(graf g, pont f, pont t) { int i; _graft *gg=(_graft*)g; if(0 <= f && f < gg->n && 0 <= t && t < gg->n) { for(i=0; (i<gg->ki[f]) && (gg->mx[gg->n * f + i] != t); i++); for(; (i<(gg)->ki[f]); i++) { (gg)->mx[(gg)->n * f + i] = (gg)->mx[(gg)->n * f + i + 1]; } >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf2.c int vanel(graf g, pont f, pont t) { int i; _graft *gg=(_graft*)g; if(0 <= f && f < gg->n && 0 <= t && t < gg->n) { for(i=0; (i<gg->ki[f]) && (gg->mx[gg->n * f + i] != t); i++); return i<gg->ki[f]; } return 0; int kifok(graf g, pont p) { if(0 <= p && p < ((_graft*)g)->n ) { return ((_graft*)g)->ki[p]; return -1; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf2.c int kielek(graf g, pont p, pont l[]) { int i,*ptr; if(0 <= p && p < ((_graft*)g)->n ) { for(i=0, ptr=((_graft*)g)->mx + p*((_graft*)g)->n+i; i<((_graft*)g)->ki[p]; i++, ptr++) { l[i]=*ptr; } return ((_graft*)g)->ki[p]; return -1; int befok(graf g, pont p) { return ((_graft*)g)->be[p]; >>> Programozás Alapjai (2008)

Programozás Alapjai (2008) graf2.c int beelek(graf g, pont p, pont l[]) { int i,*ptr; if(0 <= p && p < ((_graft*)g)->n ) { for(i=0, ptr=((_graft*)g)->mx + p*((_graft*)g)->n+i; i<((_graft*)g)->ki[p]; i++, ptr++) { l[i]=*ptr; } return ((_graft*)g)->be[p]; return -1; void megszuntet(graf g) { if(g) { free(((_graft*)g)->mx); free(((_graft*)g)->be); free(((_graft*)g)->ki); free(g); Programozás Alapjai (2008)

A memóriába töltött program részei Programkód Csak olvasható adatterület (read-only data) Globális változók területe Verem Szabad memória Programozás Alapjai (2008)

Konstansok és változók Mi a különbség a két deklaráció között? char t[]= "Tomb vagy pointer" char *p= "Tomb vagy pointer" A "Tomb vagy pointer" mindkét esetben egy konstans karaktertömb, ami a program konstans szekciójában, a csak olvasható adatok között kap helyet. Programozás Alapjai (2008)

Konstansok és változók A t egy tömb. Amikor létrejön, a veremben vagy a globális változóknak fentartott memóriaterületen lefoglalódik a megfelelő méretű memóriaterület, és a sztring értéke a csak olvasható területről bemásolódik erre a területre. A p egy pointer. Amikor létrejön, a veremben vagy a globális változóknak fentartott memóriaterületen lefoglalódik egy pointer számára megfelelő méretű memóriaterület, és ezen a területen eltárolódik a csak olvasható területen lévő sztring címe. Programozás Alapjai (2008)

Konstansok és változók Az eredmény: strcpy(t, "Hello!") A függvényhívás hatására a t területére bemásolódik a "Hello!" szöveg, azaz t felveszi ezt az értéket. strcpy(p, "Hello!") A függvényhívás hatására a p által mutatott területre bemásolódna a "Hello!" szöveg. De ez a terület csak olvasható, így futási hibát kapunk. Azt, hogy egy memóriaterület csak olvasható az operációs rendszer tartja számon (hardveres segítséggel). Programozás Alapjai (2008)

Konstansok és konstansok Mi lesz az alábbi program kimenete: Bármilyen meglepőnek tűnik is, a program nem okoz futási hibát, és a kimenet: main() { const int c = 100; int *p; p = &c; *p *= 2; printf("%d\n", c); } 200 Programozás Alapjai (2008)

Konstansok és konstansok A c tehát nem egy, a fordító számára szóló érték lesz, és nem is a csak olvasható memóriaterületen eltárolt változó, hanem egy valódi változó, egy kisebb megszorítással: nem szabad megváltoztatni az értékét (de nem lehetetlen). Azaz a fordítás során bármely direkt c=… alakú kifejezés hibát eredményez. Valódi konstansokat a C előfeldolgozóval (#define) készíthetünk. Programozás Alapjai (2008)

Bináris adatállományok Világos, hogy ha egy karakterfolyamot ASCII kódrendszerben elkészítünk és ezt egy másik, szintén ASCII kódrendszerben dolgozó gépen beolvassuk, akkor ugyanúgy tudjuk értelmezni az adatállományt. (pl. UNIX, Linux, PC) Programozás Alapjai (2008)

Bináris adatállományok Mi a helyzet bináris adatállományok esetén? Ennek megvizsgálásához készítsünk egy kis programot, amelyik 10 egész számot ír ki egy adatállományba és nézzük meg az eredményt több különböző rendszeren. Programozás Alapjai (2008)

Bináris adatállományok /* Egy bináris adatállományba egész számok kiírása. * Az adatállomány nevét a parancssorból kapjuk. * 1998. Április 24. Dévényi Károly, devenyi@inf.u-szeged.hu */ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #define DBSZAM 10 int tomb[DBSZAM]; /* innen fogunk irni */ int i; /* ciklusváltozó */ main(int argc, char **argv) { int fd; /* fájlleíró = file descriptor */ int bytedb; /* kiírt bájtok száma */ >>> Programozás Alapjai (2008)

Bináris adatállományok if ((fd = open(argv[1], O_WRONLY)) == -1) { errno = ENOENT; perror( "Megnyitási hiha" ); exit(EXIT_FAILURE); } /* a tomb feltöltése jól látható értékekkel */ for (i = 0; i < DBSZAM; i++) { tomb[i] = i + 'a'; if ((bytedb = write(fd, tomb, DBSZAM * sizeof(int))) == -1 ) { perror( "Írási hiba" ); close(fd); printf(" Sikerült kiírni %d darab bájtot\n", bytedb); Programozás Alapjai (2008)

Bináris adatállományok A Linux rendszer prompt-ját a $ jelöli (a home.cab gépen): $ gcc -o keszit keszit.c $ touch proba.bin $ keszit proba.bin Sikerült kiírni 40 darab bájtot $ 0000 00 00 00 61 00 00 00 62 00 00 00 63 00 00 00 64 ...a...b...c...d 0010 00 00 00 65 00 00 00 66 00 00 00 67 00 00 00 68 ...e...f...g...h 0020 00 00 00 69 00 00 00 6a ...i...j Programozás Alapjai (2008)

Bináris adatállományok A Linux rendszer prompt-ját a $ jelöli (a linux.inf gépen vagy az egyik munkaállomáson): $ gcc -o keszit keszit.c $ touch proba.bin $ keszit proba.bin Sikerült kiírni 40 darab bájtot $ 0000 61 00 00 00 62 00 00 00 63 00 00 00 64 00 00 00 a...b...c...d ... 0010 65 00 00 00 66 00 00 00 67 00 00 00 68 00 00 00 e...f...g...h ... 0020 69 00 00 00 6a 00 00 00 i...j ... Programozás Alapjai (2008)

Bináris adatállományok A pc-n a promptot a bill> jelöli (régen, DOS alatt): bill>keszit proba.bin Sikerült kiírni 20 darab bájtot bill> 0000 61 00 62 00 63 00 64 00 65 00 66 00 67 00 68 00 a.b.c.d.e.f.g.h. 0010 69 00 6a 00 i.j. Programozás Alapjai (2008)

Bináris adatállományok Láthatjuk, hogy a három adatállomány különböző. A Linux és DOS közötti különbség abból adódik, hogy az int adattípus nem azonos hosszúságú a két rendszerben, de ezen könnyen lehet segíteni a pontosabb típusdefinícióval. A solaris.inf, illetve a többi gép közötti különbség pedig abból adódik, hogy a bájtsorrend különbözik az egyes architektúrákon. Programozás Alapjai (2008)

Bináris adatállományok Ha az alacsonyabb című memóriahelyekre az alacsonyabb helyiértékű bájtokat tároljuk, akkor a bájtsorrend little-endian („kis indián”, pl. a PC típusú gépek). Ha az alacsonyabb című memóriahelyekre a magasabb helyiértékű bájtokat tároljuk, akkor a bájtsorrend big-endian („nagy indián”, pl. a solaris.inf szerver). Programozás Alapjai (2008)

Bináris adatállományok /* Eldönti, hogy milyen a bájtsorrend a számítógépen. * 1998. Április 24. Dévényi Károly, devenyi@inf.u-szeged.hu */ #include <stdio.h> main() { int x = 1; if (*(char *)&x == 1) { printf("little-endian\n"); } else { printf("big-endian\n"); } Programozás Alapjai (2008)

Programozás Alapjai (2008) Gyakori C hibák while (F); while (F) {} for (I;F;N); for (I;F;N) {} Üres ciklusmagok. if (F); if (F) {} Üres műveletek A ; az üres kifejezésből is utasítást csinál. Programozás Alapjai (2008)

Programozás Alapjai (2008) Gyakori C hibák Az = az értékadás, a == az összehasonlítás műveletének a jele if (a = b) {} legális utasítás sokan ezért így hasonlítanak: if (1 == x) és nem így, hogy if (x == 1) mert ha csak egy =-t írnak, akkor szól a fordító. Programozás Alapjai (2008)

Programozás Alapjai (2008) Gyakori C hibák Ha egy típus elmarad, akkor a feltételezett típus int és nem void. Minden utasítást a ; zár le, de nincs ; a blokk {} után. Nem szabad összetéveszteni a logikai és bináris, vagyis a && és & illetve a || és | műveletet. A paraméterátadás érték szerinti, vagyis bemenő módú. Ha nem ezt akarjuk, akkor az aktuális paraméterek címét kell átadni/átvenni. Programozás Alapjai (2008)

Programozás Alapjai (2008) Gyakori C hibák A printf értékeket vár, a scanf címeket. A scanf –nél nagyon fontos a méretmódosítók megadása. Ha a függvénynek nincs paramétere, akkor is ott kell lenni a ()-nak. Az ANSI szabvány szerint a deklaráció f(void), ha f-nek nincs paramétere. A tömb indexelése 0-nál kezdődik, így az n elemű tömb index tartománya 0..n-1 Programozás Alapjai (2008)

Programozás Alapjai (2008) Gyakori C hibák A többdimenziós tömb indexelése [][]-vel történik. A C érzékeny a kis- és nagybetűkre. "endianizmus", vagyis honnan származik a bináris adatállomány? A pointer deklarálásánal a *-t érdemes szorosan a változóhoz írni. Ha nem ezt tesszük, hanem így int* ptr1, ptr2; akkor nem vesszük észre, hogy ptr2 nem pointer, hanem csak int. Programozás Alapjai (2008)

Programozás Alapjai (2008) Gyakori C hibák Miért áll le hibával a következő program? struct Cellatip { Elemtip adat; /* adatelem */ struct Cellatip *csat; /* a következő */ } /* Itt lesz a főprogram */ main(int argc, char *argv[]) { ... Programozás Alapjai (2008)

Programozás Alapjai (2008) Gyakori C hibák A ~-t a shell dolgozza fel, tehát se nem abszolút és se nem relatív hivatkozás a data nevű adatállományra. (getenv("HOME")) getchar() visszaadott értéke int. Nem szabad áttenni char-ra mielőtt az EOF-al összehasonlítanánk. if ((fp=fopen("~/data", "r")) == NULL) {} Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program main(l ,a,n,d)char**a;{ for(d=atoi(a[1])/10*80- atoi(a[2])/5-596;n="@NKA\ CLCCGZAAQBEAADAFaISADJABBA^\ SNLGAQABDAXIMBAACTBATAHDBAN\ ZcEMMCCCCAAhEIJFAEAAABAfHJE\ TBdFLDAANEfDNBPHdBcBBBEA_AL\ H E L L O, W O R L D! " [l++-3];)for(;n-->64;) putchar(!d+++33^ l&1);} Az eredeti programot úgy alakítjuk át, hogy az algoritmus, amit leír, ne változzon. Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program main(l,a,n,d) char**a; { for(d=atoi(a[1])/10*80-atoi(a[2])/5-596; n="@NKA\ CLCCGZAAQBEAADAFaISADJABBA^\ SNLGAQABDAXIMBAACTBATAHDBAN\ ZcEMMCCCCAAhEIJFAEAAABAfHJE\ TBdFLDAANEfDNBPHdBcBBBEA_AL\ H E L L O, W O R L D! " [l++-3];) for(;n-->64;) putchar(!d+++33^l&1); } Kezdjük egyszerű tördeléssel, mindössze szóközöket és sorvégeket helyezünk át a programban. Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program int main(l,a,n,d) int l; char**a; int n; int d; { for(d=atoi(a[1])/10*80-atoi(a[2])/5-596; n="@NKA\ CLCCGZAAQBEAADAFaISADJABBA^\ SNLGAQABDAXIMBAACTBATAHDBAN\ ZcEMMCCCCAAhEIJFAEAAABAfHJE\ TBdFLDAANEfDNBPHdBcBBBEA_AL\ H E L L O, W O R L D! " [l++-3];) for(;n-->64;) putchar(!d+++33^l&1); } Így már látszik, hogy régi típusú C függvényleírást használtunk. A jelzett kiegészítésekkel a program működése nem változik, hiszen a C nyelv alapértelmezett típusa az int típus. Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program const char *str="@NKA\ CLCCGZAAQBEAADAFaISADJABBA^\ SNLGAQABDAXIMBAACTBATAHDBAN\ ZcEMMCCCCAAhEIJFAEAAABAfHJE\ TBdFLDAANEfDNBPHdBcBBBEA_AL\ H E L L O, W O R L D! " int main(l,a,n,d) int l; char**a; int n; int d; { for(d=atoi(a[1])/10*80-atoi(a[2])/5-596; n=str[l++-3];) for(;n-->64;) putchar(!d+++33^l&1); } Most a programban megadott hosszú sztringet kielemjük egy külön globális változóba, hogy ne zavarjon a program megértésében. Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program const char *str="@NKACLCCGZAAQBEAADAFaISADJABBA^SNL\ GAQABDAXIMBAACTBATAHDBANZcEMMCCCCAAhEIJFAEAAABAfHJET\ BdFLDAANEfDNBPHdBcBBBEA_AL H E L L O, W O R L D! " int main(l,a,n,d) int l; char**a; int n; int d; { for(d=atoi(a[1])/10*80-atoi(a[2])/5-596; n=str[l++-3];) for(;n-->64;) putchar(!d+++33^l&1); } Egy keveset elvégzünk a preprocesszor munkájából, a sztringünket kicsit összébbhúzzuk. Az értéke ezzel az átalakítással nem változik. Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program const char *str="@NKACLCCGZAAQBEAADAFaISADJABBA^SNL\ GAQABDAXIMBAACTBATAHDBANZcEMMCCCCAAhEIJFAEAAABAfHJET\ BdFLDAANEfDNBPHdBcBBBEA_AL H E L L O, W O R L D! " int main(l,a,n,d) int l; char**a; int n; int d; { for(d=(atoi(a[1])/10*80)-(atoi(a[2])/5)-596; n=str[l++-3];) { for(;n-->64;) { putchar(!d+++33^l&1); } Az átláthatóság kedvéért kiteszünk pár zárójelet. Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program const char *str="@NKACLCCGZAAQBEAADAFaISADJABBA^SNL\ GAQABDAXIMBAACTBATAHDBANZcEMMCCCCAAhEIJFAEAAABAfHJET\ BdFLDAANEfDNBPHdBcBBBEA_AL H E L L O, W O R L D! " int main(l,a,n,d) int l; char**a; int n; int d; { for(d=(atoi(a[1])/10*80)-(atoi(a[2])/5)-(7*80+36); n=str[l++-3];) { for(;n-->64;) { putchar(!d+++33^l&1); } Egy kicsit átalakítunk, hogy a 80 karakter széles terminál oszlopszáma jól látható legyen a kódban. Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program const char *str="@NKACLCCGZAAQBEAADAFaISADJABBA^SNL\ GAQABDAXIMBAACTBATAHDBANZcEMMCCCCAAhEIJFAEAAABAfHJET\ BdFLDAANEfDNBPHdBcBBBEA_AL H E L L O, W O R L D! " int main(l,a,n,d) int l; char**a; int n; int d; { int sor=80, px, py; py=(atoi(a[1])/10)-7; px=(atoi(a[2])/5)+36; for(d=py*sor-px; n=str[l++-3];) { for(;n-->64;) { putchar(!d+++33^l&1); } A külső ciklus inicializálását egy kicsit átalakítjuk, hogy érthetőbb legyen. Látszik, hogy d-be a paraméterben kapott kétdimenziós koordináta 80 oszlopot feltételező sorfolytonossá alakított értéke kerül, méghozzá úgy, hogy az origót a 7. sor 36. oszlopába toltuk el. Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program const char *str="@NKACLCCGZAAQBEAADAFaISADJABBA^SNL\ GAQABDAXIMBAACTBATAHDBANZcEMMCCCCAAhEIJFAEAAABAfHJET\ BdFLDAANEfDNBPHdBcBBBEA_AL H E L L O, W O R L D! " int main(l,a,n,d) int l; char**a; int n; int d; { int sor=80, px, py; py=(atoi(a[1])/10)-7; px=(atoi(a[2])/5)+36; for(d=py*sor-px; n=str[(l++)-3];) { for(;n-->64;) { putchar(!d+++33^l&1); } Most nézzük a sztring indexelését. Hozzávéve azt, hogy a programot 2 paraméterrel történő futtatásra tervezték, azaz az argc szerepét betöltő l értéke 3-ról indul, a ciklus egyszerűen végiglépked a sztring karakterein, egészen a 0 kódú karakterig. Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program const char *str="@NKACLCCGZAAQBEAADAFaISADJABBA^SNL\ GAQABDAXIMBAACTBATAHDBANZcEMMCCCCAAhEIJFAEAAABAfHJET\ BdFLDAANEfDNBPHdBcBBBEA_AL H E L L O, W O R L D! " int main(l,a,n,d) int l; char**a; int n; int d; { int sor=80, px, py; py=(atoi(a[1])/10)-7; px=(atoi(a[2])/5)+36; for(d=py*sor-px; n=str[(l++)-3];) { while(n-->64) { putchar(!d+++33^l&1); } A belső ciklusból csak a feltételt használjuk, így az ekvivalensen átalakítható egy while ciklussá. Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program const char *str="@NKACLCCGZAAQBEAADAFaISADJABBA^SNL\ GAQABDAXIMBAACTBATAHDBANZcEMMCCCCAAhEIJFAEAAABAfHJET\ BdFLDAANEfDNBPHdBcBBBEA_AL H E L L O, W O R L D! " int main(l,a,n,d) int l; char**a; int n; int d; { int sor=80, px, py; py=(atoi(a[1])/10)-7; px=(atoi(a[2])/5)+36; for(d=py*sor-px; n=str[(l++)-3];) { while((n--)>'@') { putchar(!d+++33^l&1); } A belső ciklus tehát annyiszor fut le, amennyivel a sztring kiolvasott karakterének kódja nagyobb a @ karakter kódjánál. Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program const char *str="@NKACLCCGZAAQBEAADAFaISADJABBA^SNL\ GAQABDAXIMBAACTBATAHDBANZcEMMCCCCAAhEIJFAEAAABAfHJET\ BdFLDAANEfDNBPHdBcBBBEA_AL H E L L O, W O R L D! " int main(l,a,n,d) int l; char**a; int n; int d; { int sor=80, px, py; py=(atoi(a[1])/10)-7; px=(atoi(a[2])/5)+36; for(d=py*sor-px; n=str[(l++)-3];) { while((n--)>'@') { putchar(((!(d++))+33)^(l&1)); } A prioritásoknak megfelelő zárójelezéssel most is láthatóvá tehetjük a kifejezés felépítését. (Ehhez persze felhasználjuk, hogy a +++ sorozatot a fordító mohó módon ++ + műveletekként értelmezi.) Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program const char *str="@NKACLCCGZAAQBEAADAFaISADJABBA^SNL\ GAQABDAXIMBAACTBATAHDBANZcEMMCCCCAAhEIJFAEAAABAfHJET\ BdFLDAANEfDNBPHdBcBBBEA_AL H E L L O, W O R L D! " int main(l,a,n,d) int l; char**a; int n; int d; { int sor=80, px, py; py=(atoi(a[1])/10)-7; px=(atoi(a[2])/5)+36; for(d=py*sor-px; n=str[(l++)-3];) { while((n--)>'@') { putchar(((!(d++))+'!')^(l&1)); } A sorfolytonos pozíciót jelző d változót lépésenként nö-veljük, majd egy negáció-val 0 vagy 1 értékre kon-vertáljuk, hozzáadjuk egy karakterkódhoz, majd l párosságától függően az utolsó bitjét megváltoztat-juk. Az ügyes karaktervá-lasztás miatt az eredmény 4-féle érték lehet. Programozás Alapjai (2008)

Programozás Alapjai (2008) Egy C program $ ./where 46 20 !!!!!!!!!!! !!! !!! !!!!!!! ! !!!!!!!!!!!!!!!!! !!!!! ! ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!! !!!! ! !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!! !!!!"!! !!!!!!!!!!!!!!!!! !! ! !!!!!!!!! !! ! !!!!!!!!!!!!!!!!!!!! ! ! !!!! ! !!!!!!!!!!!!!!!!!!!!!!!!!! !!!!! !!!!!!!!!!!!! !!! !!! ! !!!!! !!!!!!!!!! ! ! ! ! !!!!!!!! !!!!! !! !!!!!! !!!! ! !!!!! !!!! !! !!!!!!!! !! !! !! ! ! $ Ez a négyféle érték: Ha d nem nulla, akkor l párosságától függően ! vagy szóköz Ha d nulla, akkor l párosságától függően " vagy # Vagyis a sztring karakterei felváltva a szárazföld-víz szekvenciák hosszát határozták meg, míg a d 0 értéke a speciális karakter kiírásának helyét. Programozás Alapjai (2008)

Programozás Alapjai (2008) Kérdések, válaszok Programozás Alapjai (2008)