Előadást letölteni
Az előadás letöltése folymat van. Kérjük, várjon
1
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)
2
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)
3
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)
4
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)
5
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)
6
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)
7
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)
8
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)
9
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)
10
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)
11
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)
12
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)
13
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)
14
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)
15
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)
16
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)
17
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)
18
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)
19
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)
20
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)
21
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)
22
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)
23
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)
24
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)
25
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)
26
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)
27
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)
28
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, */ #include <stdio.h> #define N /* maximális elemszám */ typedef Halmaz(U) Halmaz; /* EZ ÍGY NEM C !!!*/ >>> Programozás Alapjai (2008)
29
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)
30
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)
31
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)
32
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, 2006. Augusztus 14. Gergely Tamás, */ #include <stdio.h> #define N /* maximális elemszám */ typedef int Halmaz; /* N kicsi, ezért elegendő az int */ >>> Programozás Alapjai (2008)
33
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)
34
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)
35
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)
36
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)
37
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)
38
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, * December 6. * Módosította: Gergely Tamás, * 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)
39
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)
40
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)
41
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)
42
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)
43
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)
44
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)
45
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)
46
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)
47
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)
48
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)
49
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)
50
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)
51
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)
52
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)
53
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)
54
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)
55
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)
56
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)
57
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)
58
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)
59
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)
60
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, * Február 16. * Módosította: Gergely Tamás, * Augusztus 15. */ #include <stdio.h> #include <string.h> #define L /* lapméret */ >>> Programozás Alapjai (2008)
61
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)
62
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)
63
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)
64
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)
65
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)
66
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)
67
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)
68
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)
69
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)
70
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)
71
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)
72
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)
73
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)
74
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)
75
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)
76
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)
77
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)
78
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)
79
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)
80
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)
81
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)
82
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)
83
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)
84
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)
85
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)
86
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)
87
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)
88
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)
89
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)
90
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)
91
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)
92
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)
93
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)
94
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)
95
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)
96
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)
97
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)
98
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)
99
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)
100
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)
101
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)
102
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)
103
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)
104
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)
105
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)
106
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)
107
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)
108
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)
109
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)
110
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)
111
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)
112
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)
113
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)
114
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)
115
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)
116
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)
117
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)
118
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)
119
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)
120
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)
121
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)
122
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)
123
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)
124
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)
125
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)
126
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)
127
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)
128
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)
129
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)
130
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)
131
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)
132
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)
133
Formatált I/O műveletek
d= ; -2.00; d=-2; i=-2; d= ; -1.67; d=-2; i=-1; d= ; -1.33; d=-1; i=-1; d= ; -1.00; d=-1; i=-1; d= ; -0.67; d=-1; i= 0; d= ; -0.33; d=-0; i= 0; d= ; -0.00; d=-0; i= 0; d= ; 0.33; d= 0; i= 0; d= ; 0.67; d= 1; i= 0; d= ; 1.00; d= 1; i= 0; d= ; 1.33; d= 1; i= 1; d= ; 1.67; d= 2; i= 1; d= ; 2.00; d= 2; i= 1; Programozás Alapjai (2008)
134
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)
135
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)
136
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)
137
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)
138
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, ); printf("%20f %20f\n", (long long)2005, ); } Programozás Alapjai (2008)
139
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); } bffd8d78bffd8df4 b7fabff a Programozás Alapjai (2008)
140
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)
141
Formatált I/O műveletek
printf("%llx %llx\n", , ); ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 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)
142
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)
143
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)
144
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)
145
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)
146
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)
147
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)
148
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)
149
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)
150
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)
151
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)
152
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)
153
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)
154
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)
155
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)
156
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)
157
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)
158
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)
159
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)
160
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)
161
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)
162
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)
163
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)
164
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)
165
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)
166
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)
167
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)
168
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)
169
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)
170
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)
171
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)
172
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)
173
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)
174
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)
175
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)
176
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)
177
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)
178
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)
179
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)
180
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)
181
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)
182
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)
183
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)
184
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)
185
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)
186
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)
187
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)
188
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)
189
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)
190
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)
191
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 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)
192
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)
193
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)
194
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)
195
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)
196
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)
197
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)
198
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)
199
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)
200
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)
201
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)
202
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)
203
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)
204
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)
205
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)
206
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)
207
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)
208
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)
209
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)
210
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)
211
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)
212
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)
213
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)
214
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)
215
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)
216
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)
217
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)
218
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)
219
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)
220
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)
221
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)
222
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)
223
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)
224
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)
225
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)
226
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)
227
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)
228
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)
229
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)
230
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)
231
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)
232
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)
233
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)
234
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)
235
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)
236
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)
237
Programozás Alapjai (2008)
Modulok C-ben m.h #ifndef M_H #define M_H int fuggveny(const char*); #endif Programozás Alapjai (2008)
238
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)
239
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)
240
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)
241
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)
242
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. * Augusztus 16. Gergely Tamás, */ #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)
243
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)
244
Programozás Alapjai (2008)
halmaz.c /* A halmaz modul függvényeinek megvalósítása. * Augusztus 16. Gergely Tamás, */ #include <stdlib.h> #include "halmaz.h" >>> Programozás Alapjai (2008)
245
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)
246
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)
247
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)
248
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)
249
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)
250
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)
251
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)
252
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, * December 6. * Módosította: Gergely Tamás, * Augusztus 16. */ #include <stdio.h> #include "halmaz.h" #define N >>> Programozás Alapjai (2008)
253
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)
254
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)
255
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)
256
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)
257
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)
258
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)
259
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)
260
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)
261
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)
262
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)
263
Programozás Alapjai (2008)
graf.h /* * Gráf megvalósítása típuselrejtéssel. Közös header fájl. * 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)
264
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)
265
Programozás Alapjai (2008)
graf1.c /* * Gráf megvalósítása típuselrejtéssel. * 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)
266
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)
267
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)
268
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)
269
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)
270
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)
271
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)
272
Programozás Alapjai (2008)
graf2.c /* * Gráf megvalósítása típuselrejtéssel. * 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)
273
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)
274
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)
275
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)
276
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)
277
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)
278
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)
279
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)
280
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)
281
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)
282
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)
283
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)
284
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)
285
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)
286
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)
287
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. * Április 24. Dévényi Károly, */ #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)
288
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)
289
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 $ a...b...c...d e...f...g...h a i...j Programozás Alapjai (2008)
290
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 $ a...b...c...d ... e...f...g...h ... a i...j ... Programozás Alapjai (2008)
291
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> a.b.c.d.e.f.g.h. a i.j. Programozás Alapjai (2008)
292
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)
293
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)
294
Bináris adatállományok
/* Eldönti, hogy milyen a bájtsorrend a számítógépen. * Április 24. Dévényi Károly, */ #include <stdio.h> main() { int x = 1; if (*(char *)&x == 1) { printf("little-endian\n"); } else { printf("big-endian\n"); } Programozás Alapjai (2008)
295
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)
296
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)
297
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)
298
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)
299
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)
300
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)
301
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)
302
Programozás Alapjai (2008)
Egy C program main(l ,a,n,d)char**a;{ for(d=atoi(a[1])/10*80- 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)
303
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; 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)
304
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; 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)
305
Programozás Alapjai (2008)
Egy C program const char 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)
306
Programozás Alapjai (2008)
Egy C program const char 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)
307
Programozás Alapjai (2008)
Egy C program const char 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)
308
Programozás Alapjai (2008)
Egy C program const char 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)
309
Programozás Alapjai (2008)
Egy C program const char 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)
310
Programozás Alapjai (2008)
Egy C program const char 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)
311
Programozás Alapjai (2008)
Egy C program const char 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)
312
Programozás Alapjai (2008)
Egy C program const char 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];) { { putchar(!d+++33^l&1); } A belső ciklus tehát annyiszor fut le, amennyivel a sztring kiolvasott karakterének kódja nagyobb karakter kódjánál. Programozás Alapjai (2008)
313
Programozás Alapjai (2008)
Egy C program const char 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];) { { 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)
314
Programozás Alapjai (2008)
Egy C program const char 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];) { { 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)
315
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)
316
Programozás Alapjai (2008)
Kérdések, válaszok Programozás Alapjai (2008)
Hasonló előadás
© 2024 SlidePlayer.hu Inc.
All rights reserved.