*p2; Programozás II. Horváth Ernő
ANSI C adattípusok Cím szerinti paraméter átadás Mutatók Mutatók és tömbök Mutatótömbök Dinamikus memóriakezelés Malloc, Calloc, Free, Realloc Random Typedef , Struktúrák State machine – állapotgép Linked list (láncolt lista) Fájlkezelés Helyi beállítások Időkezelés
Feladat char * 01 Feladat char * 02 Feladat valós zámok tömbbe mutatókkal Feladat memóriafoglalás Feladat - Dinamikus tömb 02 Feladat - Dinamikus tömb 03 Feladat - törlés láncolt listából Feladat - láncolt lista megfordítás Feladat – fájlolvasás Feladat - függvénymutató
Összes feladatmegoldás Összes feladatleírás
Elérhetőségek Hatwágner Ferenc Miklós http://www.sze.hu/~hatwagnf/
[email protected] Horváth Ernő http://www.sze.hu/~herno/ Tanszéki honlap http://it.sze.hu
A félév 1-3. hét
4-5. hét
6-7. hét 8-9. hét 10- 14. hét
Karaktermutatók. Karakterlánc (string) kezelő függvények. Mutatótömbök. Többdimenziós tömbök. Dinamikus memóriakezelés. Tömbök, mint függvényparaméterek. Parancssori paraméterek. Függvénymutatók. Typedef. Algoritmusok: Pontosan ellenőrzött bemenet. Struktúrák és függvények. Uniók. Bitmezők. Névterületek. Struktúratömbök pontokhoz, dátumkezeléshez. Dinamikus adatszerkezetek: listák, fák Magas szintű (stream) bemenet, kimenet. Változó paraméter lista. Fájlok másolása, egyesítése, középre igazítás. Helyi beállítások, széles karakterek, karakterláncok, időkezelés. Alacsony szintű (leírós) fájlkezelés. Könyvtárak kezelése, folymatok (process) cső (pipe) kezelése. Jelkezelés (signal), foglalatok (socket).
*p2;
Követelmények • Vizsga
– Vizsgaidőszakban Alapfeladat 2es, minden plusz feladat után egy jeggyel jobb az érdemjegy, de az alapfeladatnak működnie kell szintaktikai hiba nélkül
• ZH nincs • Beadandó feladat aláírásért » elearning.sze.hu • Katalógus nincs
Kabinet használat • Felhasználó név: EIK • Jelszó nincs • • • •
L:\ - írási jog, vizsgán ide kell a végső kódnak felkerülnie D:\ - írási jog C:\temp - ide érdemes dolgozni K:\GB_IN001_2 – vizsgán, órán ezek a feladatok használhatóak
*p2;
K meghajtó \\fs-kab.eik.sze.hu\szakkabinet\kozos\GB_IN001_2_Programozas_2
*p2;
Tömör C kód mutatókkal
On the first day of Christmas my true love ga ve to me a partridge in a pea r tree. On the second day of Christmas my true love ga ve to me two turtle doves and a partridge in a pea r tree.
*p2;
On the third day of Christmas my true love ga ve to me three french hens, two turtle doves and a partridge in a pea r tree.
#include <stdio.h> main(t,_,a)char *a;{return!0
On the fourth day of Christmas my true love ga ve to me four ca lling birds, three french hens, two turtle doves and a partridge in a pea r tree. On the fifth day of Christmas my true love ga ve to me five gold rings; four ca lling birds, three french hens, two turtle doves and a partridge in a pea r tree. On the sixth day of Christmas my true love ga ve to me six geese a-laying, five gold rings; four ca lling birds, three french hens, two turtle doves and a partridge in a pea r tree. On the seventh day of Christmas my true love ga ve to me seven swans a -swimming, six geese a-laying, five gold rings; four ca lling birds, three french hens, two turtle doves and a partridge in a pea r tree. On the eighth day of Christmas my true love ga ve to me eight maids a-milking, seven swans a -swimming, six geese a-laying, five gold rings; four ca lling birds, three french hens, two turtle doves and a partridge in a pea r tree.
On the ninth day of Christmas my true love ga ve to me nine ladies dancing, eight maids a-milking, seven swans a -swimming, six geese a-laying, five gold rings; four ca lling birds, three french hens, two turtle doves and a partridge in a pea r tree. On the tenth day of Christmas my true love ga ve to me ten lords a-lea ping, nine ladies dancing, eight maids a-milking, seven swans a -swimming, six geese a-laying, five gold rings; four ca lling birds, three french hens, two turtle doves and a partridge in a pea r tree. On the e leventh day of Christmas my true love ga ve to me eleven pipers piping, ten lords a-lea ping, nine ladies dancing, eight maids a-milking, seven swans a -swimming, six geese a-laying, five gold rings; four ca lling birds, three french hens, two turtle doves and a partridge in a pea r tree. On the twelfth day of Christmas my true love ga ve to me twelve drummers drumming, eleven pipers piping, ten lords a-lea ping, nine ladies dancing, eight maids a-milking, seven swans a -swimming, six geese a-laying, five gold rings; four ca lling birds, three french hens, two turtle doves and a partridge in a pea r tree. Press any key to continue . . .
Pointerek, más néven mutatók • A mutató olyan változó, amelyik memóriacímet tartalmaz. Létrehozása az indirekció operátorral (*) történik. • int-re mutató típusos pointer: int * p; • típus nélküli pointer: void * p; • A változó nevéből a címképző (&) operátorral kaphatjuk meg a változó címét: int x,*p; p=&x;
Forrás: A programozás alapjai - Pohl László, Budapest, 2010
*p2;
Mutatók int n,*p; p=&n; n=6; printf("%d\n", n); // n helyett írhattunk volna *p-t is *p=5; printf("%d\n", n); // n helyett írhattunk volna *p-t is
Futás: 6 5
p címe pl. 0x0028F8D8 értéke: 06
*p2;
Cím szerinti paraméter átadás void ertekSzerint(char x){ x = 22; } void cimSzerint(char *x){ *x = 33; Futás: } void main(){ Ertek szerint: 1 char e = 1, c = 1; Cim szerint: 33 ertekSzerint(e); cimSzerint(&c); printf("Ertek szerint: %d\nCim szerint: %d", e, c); }
*p2;
Csere mutatókkal void csere(int *px, int *py){ int tmp; tmp = *px; *px = *py; *py = tmp; } //a módosítandó értékek címének átadása
*p2;
Main:
int a=22, b=1020; csere(&a, &b);
printf("%d", b); Futás: 22
NULL pointer A NULL pointer egy olyan speciális mutatót jelent, amely nem mutat semmilyen változóra. Bármilyen típusú pointer lehet NULL értékű. if (ptr != NULL) printf("Valahova mutat.\n"); if (ptr == NULL) printf("Sehova sem mutat.\n"); if (ptr) printf("Valahova mutat.\n"); if (!ptr) printf("Sehova sem mutat.\n");
*p2;
Mutatók és tömbök • A mutatók jól használhatók tömbök bejárására, feldolgozására • De a mutatók nem tömbök >> memória foglalás • A tömbnevének [ ] nélküli leírása egyenértékű a 0 indexű elem címével. Például: tombom ugyanazt jelenti, mint &tombom[0]. char x[100]; char *m; m=&x[0]; m=x; *(m+1); // egyenértékű x[1]
*p2;
Mutatók és tömbök példa int tombom[10];
int *p = tombom;
*(tombom + 3) = 5; //vagy ugyanez másképp: tombom[3] = 5;
*(p + 2) = 8; //ugyanez: p[2] = 8;
Gyakorlatilag [] indexelő operátort használva címszámítást és indirekciót (dereferálást) is végzünk. A fordító a kezdőcímhez hozzáadja az adott típusnak megfelelően foglalt memóriarészeket, így éri el az adott elemet.
*p2;
Feladat legnagyobb - mutatókkal int legnagyobb(int *tomb, int meret){ int n; int max; max=tomb[0]; for( n=1; n<meret; n=n+1 ) if( max
main(){ int t[5]; t[0]=49; t[1]=40; t[2]=22; printf("Legnagyobb=%d\n" , legnagyobb(t, 3) ); getchar(); }
Feladatok char * 01 Készítse el az alább felsorolt, ismert C függvények mutatós változatát! A char * visszatérésű függvények az eredmény címével térnek vissza! • Sor beolvasása a szabvány bemnetről: int getline(char s[], int lim) int getline(char s*, int lim) • c karakter törlése a karakterláncból: void squeeze(char s[], int c) char *squeeze(char s*, int c) • Karakterlánc egésszé konvertálása: int atoi(char s[]) int atoi(char s*))
*p2;
Feladat – fehér karakter • Olvasson be a szabvány bemenetről egy karakterláncot! Egy alkalmas függvény segítségével alakítsa át úgy a beolvasott szöveget, hogy » eltávolítja belőle a fehér karaktereket, » és az első szó kivételével minden szó kezdőbetűjét nagyra, » az összes többi betűjét kisbetűre változtatja.
• Elegendő csak az angol abc jeleivel foglalkozni. Jelenítse is meg az eredményt! A programban a tömbök elemeinek elérésére mutatókat kell használni, indexelés nem fogadható el! Valami aaa BBB CcCC valamiAaaBbbCccc
extra1.c
Getline (mutatók nélkül) • Szabvány bemenetről egy sor beolvasása. • A sor karaktereit rendre elhelyezi az s karaktertömbben. A befejező soremelés karaktert nem viszi át a tömbbe, hanem helyette lánczáró '\0'–t ír a karakterlánc végére. • Legfeljebb lim karaktert tárol a tömbben, visszatérési értéke az s tömbben elhelyezett karakterlánc hossza.
*p2;
int getline(char s[],int lim){ int c, i; for(i=0; i
*p2;
Getline • s[] indexelése helyett a t mutatóval haladunk az s karaktertömbön. • A t mindig a következő szabad helyre mutat a tömbben, melynek túlírását az n elfogyása akadályozza meg. n-->0 • A karakterláncot záró '\0' címéből a tömb kezdőcímét kivonva, éppen a karakterlánc hosszát kapjuk: return(t-s)
*p2;
int getline(char *s, int n){ int c; char *t=s; while(n-- > 0 && (c=getchar())!=EOF && c!='\n') *t++ = c; *t='\0'; while(c!=EOF&&c!='\n') c=getchar(); return(t-s); }
*p2;
squeeze void squeeze(char s[], int c){ int i,j; for(i=j=0; s[i]!='\0'; i++) if(s[i]!=c) s[j++]=s[i]; s[j]='\0'; }
*p2;
*squeeze • char *squeeze(char *s, int c) • c karakter törlése az s karakterláncból a saját helyén.
*p2;
*squeeze main(){ char s[]="Ebben vannak n-ek"; printf("%s\n",squeeze(s,'n')); getchar(); }
char *squeeze(char *s, int c){ char *p,*t; p=s; t=s; while(*p){ if(*p==c) p++; else *s++=*p++; } *s=0; return(t); }
*squeeze char *squeeze(char *s, int c){ char *t, *ment=s; for(t=s; *s; ++s) if(*s!=c) *t++=*s; *t='\0'; return ment; }
*p2;
A „klasszikus” atoi int atoi(char s[]){ int i,n=0,sign=1;
for(i=0;s[i]==' '|| s[i]=='\n' || s[i]=='\t'; ++i); if(s[i]=='+'||s[i]=='-') sign=(s[i++]=='+') ? 1 : -1; for(;s[i]>='0'&&s[i]<='9';++i) n=10*n+s[i]-'0';
return(sign*n); }
*p2;
Atoi mutatókkal int atoi(char *s){ int n=0, sign=1;
for( ; isspace(*s); ++s); if(*s=='+'||*s=='-') sign=(*s++=='+') ? 1 : -1; for( ; isdigit(*s); ++s) n=10*n+*s-'0'; return(sign*n); }
*p2;
atoi • • • •
Eredeti: stdlib.h Karakterlánc egésszé alakítása (isspace >> ctype.h) Íjuk meg ennek is a mutatós változatát!
*p2;
Feladat char * 02 Készítse el az alább felsorolt, ismert C függvények mutatós változatát! A char * visszatérésű függvények az eredmény címével térnek vissza! • Karakterlánc nagybetűssé alakítása: void strupr(char s[]) char *strupr(char *s) továbbá strlwr • Karakterlánc megfordítása helyben: void strrev(char s[]) char *strrev(char *s) • Karakterlánc feltöltése c karakterrel: void strset(char s[], int c) char *strset(char *s, int c)
*p2;
strrev (mutatók nélkül) void strrev(char s[]){ int i, j; char kar; for(i = 0, j = strlen(s)-1; i<j; ++i, --j){ kar = s[i]; s[i] = s[j]; s[j] = kar; } }
*p2;
strrev
*p2;
s[0] s[1] s[2] s[3] s[4] s[5] s[6] s[7] s[8] s[9]
a
b
c
d
x
6
7
8
9
'\0'
[i=0]
9
b
c
d
x
6
7
8
a
'\0'
[i=1]
9
8
c
d
x
6
7
b
a
'\0'
[i=2]
9
8
7
d
X
6
c
b
a
'\0'
[i=3]
9
8
7
6
x
d
c
b
a
'\0'
String bejárása - mutatók • •
char *s="Szoveg"; for(;*s!='\0'; ++s) printf("%c\n", *s);
vagy röviden: •
for(;*s; ++s) printf("%c\n", *s);
*p2;
Feladat - strstr, strstnext Az strstr() függvény a második karakterlánc elsőbeli első előfordulásának címével tér vissza, ill. NULL mutatóval, ha a második karakterlánc nem található meg az elsőben. int index(char s1[], char s2[]) – char *strstr(char *, char *)
Készítsen char *strstrnext(char *, char *) függvényt, mely ugyanazt teszi, mint az strstr(), de egy static mutató segítségével az első hívás után a második karakterlánc következő elsőbeli előfordulásának címével tér vissza, és ezt mindaddig teszi, míg NULL mutatót nem kell szolgáltatnia. AZ elofordulasai AxxAZxAZxxxAyZ-ben: Megtalaltuk a 3 indexu helytol kezdodoen. Megtalaltuk a 6 indexu helytol kezdodoen. strnext.c
strstr.c
Feladat - titkosítás
*p2;
Készítsen char* titkositas(char* nyiltszoveg, char* kulcs, int hossz) függvényt, ami a titkosítja a paraméterként kapott nyílt szöveget a második paraméter kulccsal, majd visszaadja az eredményt. (Az eredmény a nyílt szöveg helyén keletkezik.) A harmadik paraméter a nyiltszoveg jeleinek száma. A titkosítást úgy végzi, hogy a nyílt szöveg első betűjének ASCII kódját kizáró vagy kapcsolatba hozza a kulcs első jelének ASCII kódjával, aztán a nyílt szöveg második jelének kódját hozza kizáró vagy kapcsolatba a kulcs második jelének kódjával, és így tovább. Ha a nyílt szöveg rövidebb lenne, mint a kulcs, akkor nem kell a kulcs minden jelét felhasználni. Ha a kulcs rövidebb, mint a nyílt szöveg, akkor a kulcsnak ismét az első, második, stb. jelét kell használni. Készítsen programot a fv. kipróbálásához! Ebben olvassa be a nyílt szöveget, majd a kulcsot, aztán jelentesse meg a titkosított szöveget! Ezt követően a már titkosított szöveget titkosítsa ismét a kulccsal! Ennek hatására az eredeti nyílt szöveget kell visszakapni. Jelenítse meg ezt is a kimeneten! Természetesen ebben a programban is csak mutatók használata megengedett, a tömbök indexelése nem! A nyilt szoveg: Jelszo Kód: ABCDEF
Kodolt szoveg: '/7?) A helyreallitott szoveg: Jelszo
Extra2.c
Mutatótömbök • Mutatókat tartalmazó tömbök • Pl. stringeket is tárolhatunk így tömbben • A main() argumentumlistája
* 0xfa0212
* 0xfc00c1
S
z
o
0xfc00b1
A
B
\0
0xfc0012
x
y
\0
[]
v
..
e
g
\0
*p2;
#include <stdlib.h> #include <stdio.h> void ir( int ennyit, char **ezt ){ int n; for(n=0; n<ennyit; ++n ) printf("%s\n", ezt[n] ); } main(){ char *tomb[3]; tomb[0]="Szoveg"; tomb[1]="AB"; tomb[2]="xyz"; ir(2, tomb ); getchar(); }
A main() argumentumlistája • Parancssori argumentumok fogadása • Minden C programban kell lennie egy a programot elindító függvénynek, mely konzol bázisú alkalmazások esetében a main függvény. • A paraméterek elhagyhatóak és nem ANSI szabványosak. • Project properties >> Debugging >> Command arguments #include <stdio.h> int main( int argc, char *argv[] ){ int n; for(n=0; n<argc; ++n) printf( "%s ", argv[n] ); printf("\n"); return(0);}
*p2;
*argv[] Az argv a paraméter karakterláncokra mutató mutatótömb, ahol az egyes elemek rendre: • argv[0]: A futó program (meghajtó névvel és) úttal ellátott azonosítójára mutató mutató. • argv[1]: Az első parancssori parameter karakterlancara mutató mutató. • argv[2]: Az második paraméter karakterlánc kezdőcíme. • argv[argc - 1]: Az utolsó parancssori paraméter karakterláncára mutató mutató. • argv[argc]: NULL mutató.
*p2;
A printf utolsó, *argv++ kifejezése
*p2;
Az argv–t a main paraméterként kapja, tehát csak címmásolat, vagyis a main–ben akár el is rontható. Az argv típusa char **, és funkcionálisan a parancssori paraméter karakterláncok kezdőcímeit tartalmazó mutatótömb kezdetének cime. A rajta vegrehajtott indirekcióval a típus char * lesz, és épp a mutatótömb első elemet (argv[0]) érjük el. Az utótag ++ operator miatt eközben az argv mar a második mutatótömb elemre (argv[1]) mutat. Elérjük ezt is, es mellékhatásként az argv megint előbbre mutat egy tömbelemmel. Tehát a ciklusban rendre végigjárjuk az összes parancssori paramétert.
Függvény (kód) mutató
*p2;
• A C nyelvben a mutatóknak két fajtája van: • (adat) objektumra mutató mutatók és • függvényre (kód) mutató mutatók
void doubleRendez(double tomb[], int meret, int (*kisebb_e)(double a, double b)); int kisebb(double a, double b) { return a < b;} int abszolutKisebb(double a, double b) { return fabs(a) < fabs(b);} . . . doubleRendez(tomb, 10, kisebb);
doubleRendez.c
Feladat - függvénymutató
*p2;
• Írjunk függvényt, amely paraméterként kap egy double elemekből álló tömböt, és rendezi azt! A rendezés szempontja (növekvő, csökkenő, abszolútérték szerint növekvő stb.) is legyen a függvény paramétere! void doubleRendez(double tomb[], int meret, int (*kisebb_e)(double a, double b))
doubleRendez.c
Dinamikus memóriakezelés A C fordító a memóriát három részre osztja: 1. heap segment » dinamikus adatterület » dinamikusan, futásidőben foglalt, változó méretű memóriablokkok 2. stack segment » verem adatterület » lokális objektumok, függvények paraméterei 3. data segment » elsődleges adatterület » globális objektumok, konstansok, statikus objektumok Az stdlib.h fejfájl dinamikus memóriakezelést megvalósító függvényei: void *malloc(m), void *calloc(a,b), void free(void *memória), void realloc(c,m) stb.
*p2;
Heap
Stack
Static / global Code (text)
Dinamikus memóriakezelés #include <stdio.h> #include <stdlib.h>
p = (int*)malloc (sizeof(int))
Global
int globEredmeny;
*p2;
free(p);
Heap
int negyzet(int x) { return x*x; }
Stack (verem)
int negyzOsszeg(int x, int y) { return negyzet(x + y); }
Static / global
Stack
Heap
void main(){ int a = 1, b = 2, *p; globEredmeny = negyzOsszeg(a, b); p = (int*)malloc(sizeof(int)); *p = globEredmeny; printf("p ereteke: %d", *p); free(p); }
negyzet x negyzOsszeg x, y
negyzOsszeg x, y
main() a, b
main() a, b
Code (text) main() a, b
int globEredmeny;
Forrás: mycodeschool
void *malloc(m), void *calloc(a,b) • A malloc-nak egy paramétere van: hány bájtot szeretnénk lefoglalni. Ezzel szemben a calloc-nak két paramétere van, és a kettő szorzata adja a kívánt bájtszámot. • A malloc által lefoglalt memóriaterület „memóriaszemetet” tartalmaz, azaz a dinamikusan lefoglalt változó kezdeti értéke bármi lehet, ahogyan ezt az egyszerű, nem dinamikus változóknál is megszoktuk. A calloc ezzel szemben a lefoglalt terület minden bájtját kinullázza. • NULL-lal tér vissza hiba esetén.
*p2;
void free(void *memória), void realloc(c,m) • A free a malloc által lefoglalt területet szabadítja fel. Első paramétere az a mutató, amit még a malloc adott vissza. • A realloc a lefoglalt terület méretét változtatja meg. Első paramétere a memóriacím, második pedig az új méret.
*p2;
memcpy, memccpy • void memcpy(void *cél, const void *forrás, size_t db);
a memóriaterület tartalmát másolhatjuk át
• void memccpy(void *cél, const void *forrás, int keres, size_t db);
a memóriaterület tartalmát másolhatjuk át, adott méret vagy hatójel alapján
*p2;
memmove, memset <string.h> • void *memmove(s, ct, n) A memmove hasonló a memcpy függvényhez, de egymást átfedő objektumok esetén is használható.
• void *memset(s, c, n) A memset elhelyezi a c karaktert az s első n karakterében és visszatérési értéke az s mutatója.
*p2;
Figyeljünk az érvénytelen mutatókra! Súlyos és veszélyes programozási hiba az érvénytelen mutatók használata, például egy free() parancs után. Elképzelhető, hogy elsőre működik a program, mert a felszabadított memóriaterületen még megtalálható a helyes érték. Például hibás kódrészlet: char *fuggveny() { char c = 'a'; return &c; // hiba! } char *p = fuggveny(); // hiba!
*p2;
sizeof A sizeof egyoperandusos magas prioritású művelet, mely az operandusa tárolásához szükséges memória mennyiségét szolgáltatja bájtban. • sizeof(egyoperandusos-kifejezés) pl. sizeof(tomb) • sizeof(típusnév) pl. sizeof(int)
Az egyoperandusos kifejezés típusát a fordító a kifejezés kiértékelése nélkül határozza meg, azaz ha az operandus tömbazonosító, az egész tömb bájtokban mért helyfoglalásához jutunk.
*p2;
2d tömb indexelése
*p2;
int tomb2d[2][4]={{1,2,3,4},{5,6,7,8}}; Indexelés: tomb2d[i][j] máshogy: • *(tomb2d[i] + j) • (*(tomb2d + i))[j] • *((*(tomb2d tomb2d+ i)) + j) • *(&tomb2d[0][0] + 4*i + j)
Tömbreprezentáció: 1
2
3
4
5
6
7
8
A memóriában, sorfolytonosan: 1
2
3
4
5
6
7
8
Feladat - dinamikus mátrix
*p2;
• Allokáljon memóriát egy 𝑛 𝑥 𝑛 méretű int típusú adatokat tartalmazó mátrix számára, a főátlót töltse fel egyesekkel, a többi elem legyen nulla. Jelezze is ki az értékeket.
• Főátlónak a bal felső és a jobb alsó sarkot összekötő átlót hívjuk. A példa 𝑛 = 5 esetét mutatja.
dinmatrix01.c
'dinmtx' elemei: 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1
Feladat - 2d dinamikus tömb • Írjon olyan függvényt, amely cím szerint átvett paraméterben adja vissza a lefoglalt 2D dinamikus tömbre mutató pointert! void foglal(float ***ptomb, int szeles, int magas)
• Figyeljünk arra, ha a változó típusa float **, akkor a címének típusa float ***, így cím szerinti paraméterátadásnál is így kell használni.
dinmatrix02.c
*p2;
Feladat - 2d dinamikus tömb megoldás void foglal(float ***ptomb, int szeles, int magas) { float **uj; int y, z; uj = (float**)malloc(magas * sizeof(float*)); for (y = 0; y < magas; ++y){ uj[y] = (float*)malloc(szeles * sizeof(float)); for(z = 0; z < szeles; ++z){ uj[y][z] = (float)(y + z); //printf("%.1f\n", uj[y][z]); } } *ptomb = uj; // *ptomb >> a float** típusú változó! }
*p2;
Feladat - Mátrix elemei és címeik
*p2;
'dinmtx' elemei (es cimeik): dinmtx[0]: (00825AB8) [0][0]=1 (0082A748) [0][1]=2 (0082A74C) [0][2]=3 (0082A750) dinmtx[1]: (00825ABC) [1][0]=4 (0082A790) [1][1]=5 (0082A794) [1][2]=6 (0082A798) dinmtx[2]: (00825AC0) [2][0]=7 (0082A7D8) [2][1]=8 (0082A7DC) [2][2]=9 (0082A7E0)
dinmatrix03.c
Random - Véletlenszám #include <stdio.h> #include
#include <stdlib.h> #define MAX 20
void main(){ int r = 0, i; srand(time(NULL)); for(i=0; i<20; i++){ r = (rand() % MAX); printf("%d\n", r); } getchar(); }
Álvéletlen számok (pseudo-random numbers)
*p2;
Random - Véletlenszám A véletlenszám-generátor használata: program elején inicializálni kell (srand) egyszer, és utána a rand() ad egy véletlen számot. A %100 hatására 0..99 között lesz, ehhez 1-et adva kapjuk az 1..100 tartományt. A működéshez a stdio.h, time.h, stdlib.h headerek szükségesek.
srand(time(NULL)); r = rand()%100 + 1;
*p2;
Feladat - mátrix összeadás
*p2;
Készítsen programot két mátrix összeadására! A mátrixoknak foglaljon helyet a memóriában! A mátrixok mérete azonban csak futás időben dől el. A számok beolvasásához felhasználható az int getint(int *) függvény, de feltétlenül lássuk el minimális inputellenőrzéssel!
mtx.c
Feladat - Bűvös mátrix (magic square) Olyan (ált. 1 és n2 közötti) egész számokat tartalmazó négyzetes mátrix, melynek minden sorösszege, minden oszlop összege, főátlójában és mellékátlójában lévő számok összege azonos. https://en.wikipedia.org/wiki/Magic_square 8 3 4
1 5 9
6 7 2
Buvos matrix? Igen
117 104 27
34 1 49
115 63 29
Buvos matrix? Nem buvosMatrix.c
*p2;
Feladat - Sudoku generálás
*p2;
Generáljunk egy 9 x 9-es tömböt, úgy, hogy 1-től 9-ig legyenek benn számok, de tetszőleges sorban, oszlopban mindegyik szám csupán egyszer forduljon elő. Megjegyzés: a játékban ennél több szabály van. Megoldható *p és **p megvalósítással. 1 2 3 4 5 6 7 8 9
2 3 4 5 6 7 8 9 1
3 4 5 6 7 8 9 1 2
4 5 6 7 8 9 1 2 3
5 6 7 8 9 1 2 3 4
6 7 8 9 1 2 3 4 5
7 8 9 1 2 3 4 5 6
8 9 1 2 3 4 5 6 7
9 1 2 3 4 5 6 7 8
3 4 5 6 7 8 9 1 2
9 1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8 9
4 5 6 7 8 9 1 2 3
2 3 4 5 6 7 8 9 1
7 8 9 1 2 3 4 5 6
6 7 8 9 1 2 3 4 5
5 6 7 8 9 1 2 3 4
8 9 1 2 3 4 5 6 7
7 4 1 6 9 8 3 5 2
sudoku1d.c
4 1 7 3 6 5 9 2 8
5 2 8 4 7 6 1 3 9
8 5 2 7 1 9 4 6 3
6 3 9 5 8 7 2 4 1
sudoku2d.c
2 8 5 1 4 3 7 9 6
1 7 4 9 3 2 6 8 5
9 6 3 8 2 1 5 7 4
3 9 6 2 5 4 8 1 7
2D tömb megvalósítások 1.
Sorfolytonosan
2. Soronkénti foglalás
3. Leképezés és mutatótömb "jagged array"
*p2;
Soronkénti foglalás egydimenziós mutatótömbbel *p2; #include <stdio.h> #define MERET 3 void main(){ char* muttomb[MERET] = {"Egy","két","há"}; int i; printf("'muttomb' elemei: \n"); for (i = 0; i<MERET; i++) printf("%s (%p) (%p)\n", muttomb[i], &muttomb[i], &muttomb[i][0]); printf("\n"); getchar(); }
'muttomb' elemei: Egy (0114FE18) (01345858) két (0114FE1C) (0134585C) há (0114FE20) (01345860)
Memóriában: 0x0114FE18: 0x01345858: 0x01345858: 0x0134585C: 0x01345860:
01345858 0134585c 01345860 cccccccc 00796745 0074e96b 0000e168 20756d27 Egy.két.há.. két.há.....x há.....xxxxx
Feladat valós számok tömbbe, mutatókkal Készítse el a PELDA24.C-hez hasonlóan a valós számok tömbbe olvasását és visszaírását! Az int getint(int*) függvény int getfloat (float *)-ra változtatandó! Az int bufp; lecserélendő char *bufp;-re! Tegyünk legalább annyi inputellenőrzést a getfloat-ba, hogy legalább egy numerikus karakter meglétét megkövetelje, ill. konvertálhatatlan karakterek esetén ürítsük ki a bemeneti puffert a legközelebbi fehér karakterig!
*p2;
Struktúrák • Összetett adattípus, mint a tömbök, de nem csak egyféle típus alkothatja őket • Változók csoportjának egyszerűbb kezelése • Lehetőségek: » hozzárendelés (másolás) » átadhatók függvénynek paraméterként » lehet függvény visszatérési értéke » képezhető méretük (sizeof) és címük (&) » nem lehetséges: összehasonlítás (csak tagonként) » struktúratag szinte bármiből lehet
*p2;
Struktúrák #include <stdio.h> struct Pont{ int x; int y; int z; char nev[32]; }; void main(){ struct Pont p1, p2; p1.x=8; p1.y=5; }
A ; nem véletlen a struktúrák után.
*p2;
Typedef #include <stdio.h> typedef int egesz; void main(){ egesz e = 42; printf("e=%d\n", e); }
Új típust a typedef kulcsszóval vezethetünk be. Az egesz típus közvetlenül az int típusból származik. Persze ez így még nem túl hasznos.
*p2;
Typedef és struktúra #include <stdio.h> struct Pont{ int x; int y; int z; }; typedef struct Pont pont; int kiir( pont *p ){ printf("x=%d\ny=%d\nz=%d\n",p->x, p->y, p->z); } void main(){ pont p1, p2; p1.x=1; p1.y=42; p1.z=84; kiir(&p1); }
A Pont egy struktúra, a pont pedig típusdefiníció. A ; nem véletlen a struktúrák után. typedef struct Pont { int x; int y; Elhagyható – int z; névtelen } pont; struktúra.
*p2;
Struktúrák typedef struct { //egy pont a síkban double x; double y; } Pont; typedef struct { //szakasz két pont közt Pont eleje; Pont vege; } Szakasz; typedef struct { //középpont és sugár Pont kozeppont; double sugar; } Kor;
*p2;
Structure dereference operator Structure dereference (röviden: nyíl) operátor jelentése a mutató által mutatott struktúra adattagja. Egyrészt dereferál, másrészt adattagot is kiválaszt, így rövidebb. (*p).adattag vagy rövidebb jelöléssel: p->adattag A pont "." adattag kiválasztó operátor nagyobb precedenciájú, mint a "*" índirekció operátor. Ezért is kell zárójelezni, a nyíl operátor erre ad könnyebb módot.
*p2;
Feladat - szakasz Írjon programot, mely a jelzett adatstruktúrákat használja. Egy double szakaszHossza(Szakasz *sz) prototípusú függvény visszatérési értékeként számolja ki a megadott szakasz hosszát!
*p2;
typedef struct{ double x; double y; }Pont; //szakasz két pont közt typedef struct{ Pont eleje; Pont vege; }Szakasz;
Megoldás: szakasz.c
Feladat - osztálytárs
*p2;
Írjon programot, mely a jelzett adatstruktúrákat használja. A feladatban tartsuk számon, hogy az osztálytársak kit tartanak a legjobb barátjuknak. Írjon függvényt, mely kiírja, hogy az adott személy barátsága kölcsönös-e. Továbbá listázza az összes személy barátait. typedef struct{ char nev[32]; int legjobbbarat; } diak; // strukturákból alló tömb diak osztalytarsak[10];
// kölcsönös-e a barátság int kolcsonos(int szemely){ if (osztalytarsak[osztalytarsak[szemely].legjobbbarat].legjobbbarat = szemely) return 1; else return 0; }
Megoldás: osztalytars.c
Feladat - Dinamikus tömb 02
*p2;
Írjon programot, amely bekéri, egy sokszög csúcsainak számát, ezután pedig egyesével a csúcsok koordinátáit! A koordinátákat egy struktúrában tárolja, melynek dinamikusan foglaljon memóriát. Inputellenőrzés! (lebege, egesze) typedef struct{ double x, y; A struktúra méretét megadja az n*sizeof(Pont) kifejezés. } Pont; sokszog=(Pont*)malloc(n*sizeof(Pont)); Pont *sokszog; Megoldás: dinstrukt02sokszog.c
Feladat - Dinamikus tömb 03 Írjon programot, amelyben egy int valogat(double *eredeti, int meret, double **ujtomb, int *ujmeret)
függvény egy paraméterben kapott double tömbből kiválogatja az átlagnál kisebb értékeket, és egy újonnan lefoglalt dinamikus tömbbe rakja azokat! Az új tömb címével és méretével térjen vissza cím szerint átadott paraméterben! A visszatérési érték legyen IGAZ, ha sikerült a művelet, és HAMIS, ha nem. Megoldás: dinstrukt03valogat.c
*p2;
Uniók
*p2;
Hasonlít a struktúrákra, de a tagok azonos tárcímen kezdődnek » az uniót címző mutató egyben valamennyi tagot is címzi (de a mutató típusok eltérőek lehetnek) Unió tárigénye = legnagyobb tag tárigénye #include<stdio.h> void main() { union byte { unsigned negy; unsigned short ket[2]; unsigned char egy[4]; } adat = { 0x0103070Ful }; printf("'adat' cime: %p, merete: %lu byte\n" "'adat.negy' cime: %p, merete: %lu byte, erteke: %08x\n" "'adat.ket' cime: %p, merete: %lu byte, ertekei: %04x; %04x\n" "'adat.egy' cime: %p, merete: %lu byte, " "ertekei: %02x; %02x; %02x; %02x\n", &adat, sizeof(adat), &adat.negy, sizeof(adat.negy), adat.negy, &adat.ket, sizeof(adat.ket), adat.ket[0], adat.ket[1], &adat.egy, sizeof(adat.egy), adat.egy[0], adat.egy[1], adat.egy[2], adat.egy[3]); }
'adat' cime: 0037FD04, merete: 4 byte 'adat.negy' cime: 0037FD04, merete: 4 byte, erteke: 0103070f 'adat.ket' cime: 0037FD04, merete: 4 byte, ertekei: 070f; 0103
'adat.egy' cime: 0037FD04, merete: 4 byte, ertekei: 0f; 07; 03; 01
Linked list (láncolt lista) • Olyan önhivatkozó adatszerkezet, ahol az egyes elemek láncba vannak fűzve azáltal, hogy tárolják a soron következő elem címét. typedef struct elem{ //egy listaelem unsigned adat; //nem csak egy elem lehet struct elem *kov; //itt még kell a struct } elem;
*p2;
Egyszerű láncolt lista typedef struct elem { char nev[100]; unsigned eletkor; struct elem *kov; } elem_t; elem_t *kezd;
*p2;
//név //életkor
kezd = malloc(sizeof(elem_t)); kezd->eletkor = 19; kezd->kov = malloc(sizeof(elem_t)); kezd->kov-> eletkor = 88;
Megjegyzés: A typedef segítségével létrehozunk egy rövidebb nevet, a struktúrán belül mégis a struct kulcsszót kell használni, hiszen ott még nem létezik az alternatív név. Ebben az esetben tehát nem használhatjuk a névtelen struktúrát.
Láncolt lista bejárása 1. elem címe: eleje 2. elem címe: eleje->kov (*eleje).kov 3. elem címe: eleje->kov->kov (*(*eleje).kov).kov 4. elem címe: eleje->kov->kov->kov (*(*(*eleje).kov).kov).kov
ListaElem *eleje
0xcc6
0xcc6
0xcc1
0xcb0
0xcca
1
2
3
4
0xcc1
0xcb0
0xcca
NULL
*p2;
A lista bejárása
*p2;
ListaElem *iter; for (iter = eleje; iter != NULL; iter = iter->kov) printf("%d ", iter->szam);
Nézzünk erre egy példát! lancolt00alap.c
ListaElem *iter
ListaElem *eleje
0xcc6
0xcc6
0xcc1
0xcb0
0xcca
1
2
3
4
0xcc1
0xcb0
0xcca
NULL
Feladat – láncolt lista létrehozás, bejárása • Írjon rövid C programot, amely létrehozza a jelzett adatstruktúrában a láncolt listát, majd rendre kiíratja az elemeit. [1] [17] [12] [14] [8]
[0066A820] [0066A7D8] [0066A790] [0066A748] [00665AB8]
printf("[%d]\t[%p]\t[%p]\n", iter->adat,
iter,
typedef struct Lista { int adat; struct Lista *kov; }Lista;
[0066A7D8] [0066A790] [0066A748] [00665AB8] [00000000]
iter->kov); lancolt00alap.c
Törlés láncolt listából • Törlés a lista elejéről: » az előtte lévő mutató kov elemét az utána lévőre állítjuk » felszabadítjuk a törlendőt
• Törlés algoritmusa a lista belsejéből: » megkeressük a törlendő elemet » az előtte lévő mutató kov elemét az utána lévőre állítjuk » felszabadítjuk a törlendőt
*p2;
Törlés tömbből/láncolt listából És ha ez sokkal több elem?
[0]
[1]
[2]
[3]
[4]
[5]
Tömb
Műveletigény? [0]
Láncolt lista
[1]
[2]
[3]
[4]
X
Műveletigény?
*p2;
Tömbök/láncolt listák
*p2;
• tömbök előnye:
» a sorfolytonos tárolás miatt közvetlenül címezhető » gyors adatelérés
• tömbök hátránya:
» lassú átméretezés » nem ideális beszúrás/törlésintenzív feladatokra
• láncolt listák előnye:
» gyors átméretezés: beszúrás, törlés
• láncolt listák hátránya: » nincs közvetlen elérés
0
1
2
3
4
0
1
2
3
Feladat - törlés láncolt listából • Írjon programot, amely a beolvasott karakterláncokat (stringeket) láncolt listában tárolja el. Törölje ki belőle azokat, amelyek pontosan öt betűből állnak.
typedef struct Szo { char szo[MAX+1]; struct Szo *kov; } Szo;
• Figyeljünk, arra, ha az első elemet szeretnénk törölni, a kezdőcím is megváltozik (sőt ha az első N darabot, akkor is). • Gondoljuk át, hogy nézne ki az algoritmus láncolt listák helyett, többdimenziós tömbökkel. Megoldás: lancolt01torol.c
Beszúrás a lista elejére Amit tudnia kell a függvénynek: • a lista elejére mutató pointert • az új, eltárolandó adatot Az algoritmus: • az új elemnek memóriát allokálni, adatait beállítani, a "kov" lesz az új lista elejére mutató pointer • üres lista esetén az új elem egymaga lesz a lista • meg kell tudnia változtatni az "eleje" mutatót, ezzel tér vissza Lista *lisE = NULL; lisE = listaElejere(lisE, 5); lisE = listaElejere(lisE, 10);
*p2;
Beszúrás a lista elejére
*p2;
//lista elejére tesz számokat Lista *listaElejere(Lista *eleje, int adat) { Lista *uj=(Lista*)malloc(sizeof(Lista)); uj->kov = eleje; 0xcb0 0xcc1 uj->adat = adat; 2 3 0xcc1 0xcb0 0xcca Lista *eleje eleje = uj; return eleje; 0xcc6 0xcc1 0xcb0 } 0xcc6 Lista *eleje
0xcca 4 NULL
0xcca
1
2
3
4
0xcc1
0xcb0
0xcca
NULL
Lista *uj
Beszúrás a lista végére
*p2; 0xcc1
0xcb0
Amit tudnia kell a függvénynek: 2 3 • a lista elejére mutató pointert 0xcc1 0xcb0 0xcca • az új, eltárolandó adatot Az algoritmus: • az új elemnek memóriát allokálni, adatait beállítani, a "kov" értéke most NULL lesz • üres lista esetén az új elem egymaga lesz a lista • végigiterálunk a listán és megkeressük az utolsó elemet • az utolsó elem "kov" mutatóját beállítjuk az új elem címére Lista *lisV = NULL; lisV = listaVegere(lisV, 5); lisV = listaVegere(lisV, 10);
0xcc1
0xcca 4 NULL
0xcc1
0xcb0
0xcca
0xc00
2
3
4
5
0xcb0
0xcca
0xc00
NULL
Beszúrás a lista végére //lista végére tesz számokat Lista *listaVegere(Lista *eleje, int adat) { Lista *mozgo, *uj; uj=(Lista*)malloc(sizeof(Lista)); uj->adat = adat; uj->kov = NULL; if (eleje == NULL)//üres lista? return uj; for(mozgo = eleje; mozgo->kov!=NULL; mozgo=mozgo->kov) ; //üres ciklus, megkeresi az utolsó elemet mozgo->kov = uj; return eleje; }
*p2;
Feladat - láncolt lista megfordítás • Fordítsunk meg egy listát az elemei átláncolása által. Írjon programot, mely számokat olvas be, és kiírja a keletkező listát eredeti sorrendjében (és ezáltal a számokat eredeti sorrendjükben), továbbá kiírja a megfordítottat is. • Mindig a lista elejéről veszünk el egy elemet, majd a megfordított lista elejére betesszük azt, így a lista megfordul. Végül az eredeti lista elfogy, amint ez megtörténik, a megfordított lista kész. Megoldás: lancolt02megfordit.c Forrás: A programozás alapjai - Pohl László, Budapest, 2010
*p2;
Láncolt lista megfordítása Szam *listaMegfordit(Szam *lista) { Szam *eredeti=lista; Szam *megforditott=NULL; while (eredeti!=NULL) { Szam *atrakott=eredeti; Szam *kovetkezo=eredeti->kov; 1 atrakott->kov=megforditott;//új elejére be 2 megforditott=atrakott; 3 eredeti=kovetkezo;//régiből kihagy } return megforditott; }
átrakott következő 3 eredeti
2
1
megfordított
Mindig a lista elejéről veszünk el egy elemet, majd a megfordított lista elejére betesszük azt, így a lista megfordul. Végül az eredeti lista elfogy, amint ez megtörténik, a megfordított lista kész.
*p2;
Kétszeresen láncolt lista • Doubly linked list • Hátrafelé és előrefelé is be tudjuk járni » Az egyszeresen láncoltat csak előre
• Két plusz "strázsa" elem, amelynek nem tartalmaznak hasznos adatot, tehát a hasznos adatok listája az eleje ("első") strázsa utáni elemtől a vége ("utolsó") elemig tart.
*p2;
typedef struct elem{ unsigned adat; struct elem *kov, *elozo; } elem;
További listás megvalósítások • queue, FIFO » first in, first out
» pl. producer-consumer (termelőfogyasztó probléma)
• stack, LIFO » last in, first out » pl. syntax parsing
• circular list
» pl. round-robin
*p2;
State machine - állapotgép • Az állapotgép egy gyakran használt tervezési minta (design pattern), • A lényege, hogy az adott alrendszer csak egy állapotban lehet egyszerre, ebből az átmenetek és az egyes állapotok tevékenységei pontosan definiáltak. • Megadható állapot és tevékenységtáblás, UML diagram és nem szabványos módokon. » http://en.wikipedia.org/wiki/Finite-state_machine » http://en.wikipedia.org/wiki/UML_state_machine
*p2;
Szövegfeldolgozó állapotgéppel • Tegyük fel, hogy nem tudjuk, milyen hosszú egy sor » nem olvashatunk tömbbe egy sort » karakterenként olvassunk
• Szűrjük ki a /*-gal kezdőtő és */-ig tartó kommenteket Szöveg* valami /*komment ** és /*/ hh /*a*/ szöveg
Forrás: A programozás alapjai - Pohl László, Budapest, 2010
*p2;
4 állapotú state machine implementáció case cs_var: if(c=='*') a = komment; else{ putchar('/'); if(c!='/'){ putchar(c); a=normal; } } break; case komment: if(c=='*') a = p_var; break; case p_var: if(c=='/') a = normal; else if(c!='*') a = komment; break; } }
//state machine - állapotgép #include <stdio.h> typedef enum {normal,komment,cs_var,p_var} allapotok; int main(void){ int c; allapotok a = normal; //CTR+Z-ig
while((c=getchar())!=EOF){ switch (a){ case normal: if(c!='/')putchar(c);
else a = cs_var; break; }
Forrás: A programozás alapjai - Pohl László, Budapest, 2010
Komment /* */ szűrő Állapottábla Állapot normál csillagra vár megjegyzés perre vár
’*’ normál megjegyzés perre vár perre vár
*p2;
’/’ csillagra vár csillagra vár megjegyzés normál
egyéb normál normál megjegyzés megjegyzés
’/’ nem másol előző ’/’ kiírása, nem másol
egyéb másol előző ’/’ kiírása, másol
Tevékenységtábla Állapot normál csillagra vár
’*’ másol nem másol
Forrás: A programozás alapjai - Pohl László, Budapest, 2010
Komment /* */ szűrő egyéb, '*'
normal
'/'
egyéb, '/'
'/'
cs_var
*p2;
'*'
egyéb
megj
'*'
'*'
p_var
egyéb '/'
Az implementációba be lehet tenni egy plusz állapotot - végállapotot -, amibe bármikor elérhetünk EOF hatására.
Helyi beállítások • locale.h fejfájl ANSI/ISO szabványos típusok, szimbolikus állandók és függvények használatához. • Ország, nyelv, dátum formátuma, pénznem, stb. Ezeket az összetevőket helyi kategóriáknak nevezzük. Szimbolikus állandók, a program helyi információjának mely részéről van szó. • A hely bizonyos vonatkozásait a könyvtári rutinok automatikusan kezelik. Például: strcoll, strxfrm. • Más vonatkozások viszont kívül esnek a rutinok hatáskörén. Például a (hiba)üzenetek nyelve. • Alapértelmezés a "C" hely, és programindításkor. char *setlocale(int kategoria, const char *locale);
*p2;
Helyi beállítások • Francia Kanadához ANSI alapértelmezett kódlapot állít. setlocale(LC_ALL, "French_Canada.ACP"); • Francia Kanadához OEM alapértelmezett kódlapot állít. setlocale(LC_ALL, "French_Canada.OCP"); • Alapértelmezés: setlocale(LC_ALL, "C"); setlocale(LC_ALL, NULL);
• Az operációs rendszer értékei: setlocale(LC_ALL, ""); • A következő, két setlocale hívás funkcionálisan ekvivalens: setlocale(LC_ALL, "English"); setlocale(LC_ALL, "English_United States.1252");
*p2;
Helyi beállítások • A struct lconv tagjai leírják, hogyan kell formázni a numerikus és a pénzügyi értékeket, mint például a nemzetközi pénznem szimbólumát, a decimális pontot stb. • A struct lconv *localeconv(void) az aktuális helyi numerikus és pénzügyi beállításokat egy statikus lconv struktúrában helyezi el, és visszaadja ennek címét. • A setlocale kategória paramétere nem csak LC_ALL, hanem többek között LC_TIME (dátum és idő beállításokra hat) vagy LC_MONETARY (pénzügyi beállításokra hat) is lehet.
*p2;
Helyi beállítások
*p2;
//Alapbeállítások printf("\nHely: %s\n", setlocale(LC_ALL, NULL)); printf("Szam: %.2f\n\n", atof("3.14")); //Tizedespont //Alapértelmezett hely beállítása strcpy(puff, setlocale(LC_ALL, "")); printf("\nHely: %s\n", puff); printf("Szam: %.2f\n\n", atof("3,14")); //Tizedesvessző // Tizedespont és pénznem karakter: plconv = localeconv(); printf("Nem penzugyi decimalis pont: %s\n", plconv->decimal_point); printf("Penznem szimbolum: %s\n", plconv->currency_symbol); // A hely visszaállítása C környezetre: setlocale(LC_ALL, "C");
Elérhető: helyi01.c
Karakterkódolás, ékezetek • Az egybájtos karakterkódolások:
» ASCII » 128 karakter, 7 bit (2^7 = 128) » Latin-1 » másnéven ISO8859-1 » az első 128 ugyanaz, mint az ASCII, de, utána még 96 betűt használ (160-255 tartományban), így is belefér az egy bájtba (2^8 = 256) » nincs benn magyar ő és ű csak û és õ » Latin-2 » hasonló a Latin-1-hez, de ebben a magyar ő és ű is megtalálható » Windows-1250 » ez hasonlít a Latin-2-hez, többek közt a Windows szöveges fájok is használják, konzolos alkalmazásoknál előfordul, célszerű ezt használnunk C programok esetében. » OEM-852 » konzolban szintén előfordulhat (a, de nem hasonlít a Latin2-höz
*p2;
Karakterkódolás, ékezetek • A többbájtos karakterkódolások: » Unicode » Az első 128 karaktere ugyanaz, az ASCII-é, de több, mint 120000 karaktert tartalmaz a világ majdnem összes nyelvén. 2 bájtos, így már nem fér bele a char adattípusba, egybájtosról viszonylag könnyen átalakítható kódolás, de vissza már kevésbé, hiszen egy Unicode karakter nem biztos, hogy létezik pl.: Latin-2-ben. További probléma, hogy a 2 bájtot nem minden architektúra ugyanabban a sorrendben tárolja. Ezt a BOM (byte order mark) hivatott jelezni, mely vagy 0xFEFF vagy 0xFFFE. (C90: wchar_t típus » 16 biten kódolja a karaktereket) » UTF-8 » Változó méretű karakterkódolás, 1 bájtos egységeket használ, így nem csak sorrendben, de tényleges bináris kódban is kompatibilis az ASCIIvel, hiszen az egy bájtba beleférő értékeket egy bájton is tárolja.
*p2;
Ajánlott beállítások Windows környezetre • A magyar nyelvű Windowsban a programok nagy része Windows-1250 karakterkódolást használ, ez a Latin-2-vel sok hasonlóságot mutat. • Első lépésként a Visual Studioban át kell állítanunk a forrásfájl kódlapját (File » Advanced Save Options) #if defined(WIN32) || defined(_WIN32) #include <windows.h> • Ezután a konzol ki és bemeneti #endif #if defined(WIN32) || defined(_WIN32) kódlapját kell átállítani. (Nem SetConsoleCP(1250); szabványos, Windows specifikus) SetConsoleOutputCP(1250); #endif
*p2;
Helyi beállítások és ékezetek #include <stdio.h> #include <stdlib.h> #include <string.h> #include #if defined(WIN32) || defined(_WIN32) #include <windows.h> #endif void main() { struct lconv *plconv; char puff[256]; strcpy(puff, setlocale(LC_ALL, NULL)); plconv = localeconv(); // Beállítások nélkül printf("\nHely: %s\n", puff); printf("Nem pénzügyi decimális pont: %s\n", plconv->decimal_point); printf("Árvíztűrő tükörfúrógép\n\n"); // Op.rendszer által használt nyelvi környezet strcpy(puff, setlocale(LC_ALL, "")); plconv = localeconv(); printf("Hely: %s\n", setlocale(LC_ALL, puff)); printf("Nem pénzügyi decimális pont: %s\n", plconv->decimal_point); printf("Árvíztűrő tükörfúrógép\n"); printf("Pénznem szimbólum: %s\n", plconv->currency_symbol); // A konzolablak kódlapjának beállítása (out és in) #if defined(WIN32) || defined(_WIN32) SetConsoleCP(1250); SetConsoleOutputCP(1250); #endif printf("Gépeljen szöveget:\n\n"); getline(puff, 255); printf("A begépelt szöveg: [%s]", puff); getchar(); }
*p2;
Feltételezzük a magyar Windows operációsrendszert Hely: C Nem pÚnz³gyi decimßlis pont: . ┴rvizt¹r§ t³k÷rf·r¾gÚp Hely: Hungarian_Hungary.1250 Nem pénzügyi decimális pont: , Árvíztűrő tükörfúrógép Pénznem szimbólum: HUF Gépeljen szöveget:
AbcÁÉŰőö A begépelt szöveg: [AbcÁÉŰőö] Elérhető: helyi02.c
Magas szintű bemenet, kimenet • Absztrakt módon, fájlok és eszközök (billentyűzet, képernyő, nyomtató, stb.) rugalmas, hasonló módon történő független kezelése ún. folyamok (stream) segítségével.
*p2;
Fájlkezelés • Az stdio.h-ban megadott FILE* típusú pointerrel és függvényekkel. FILE *fp; • Megnyitás módja: írás (w) v. olvasás (r), szöveges (t) v. bináris (b), hozzáfűzés (a) írás és olvasás (w+) fp=fopen("t.txt", "wt"); • Az fopen() visszatérési értéke: hivatkozás a nyitott fájlra. Sikertelen megnyitásnál értéke: NULL pointer – ezt ellenőrizni kell. if (fp == NULL) printf("Nem sikerült megnyitni!");
*p2;
fopen - fclose FILE *fopen(const char *fajlazon, const char *mod); • A függvény megnyitja a fajlazonnal megnevezett fájlt, és folyamot kapcsol hozzá. Visszaadja a fájlinformációt tartalmazó FILE struktúrára mutató pointert, mely a rákövetkező műveletekben azonosítani fogja a folyamot, ill. NULL mutatót kapunk tőle, ha a megnyitási kísérlet sikertelen volt. • Az első paraméter legfeljebb FILENAME_MAX méretű karakter lehet. • Lezáráshoz: int fclose(FILE *fp) - 0-t ad vissza siker esetén; EOF-ot, ha nem sikerült lezárni
*p2;
Fájlok kezelése Szöveges • 48 65 6c 6c 6f 2c 20 76 69 6c 61 67 21 0d 0a 30 0d 0a 31 0d 0a • Hello, vilag! 0 1 • fgets, fgetc, fscanf ...
Bináris • 6c cc cc cc 55 8b 8b 45 0c 83 f8 04 23 8b 0c 85 00 80 • lÌÌÌU;>ì-ƒø.w#å4Â
• fread, fwrite ...
*p2;
ec 77 c5 ہ
Szöveges fájlok
*p2;
• A szövegfájlokat az fprintf()-fel és az fscanf()-fel lehet kezelni. Ezeknek első paramétere a megnyitott fájl, a folytatás pedig ugyanúgy van, mint printf()-nél és az scanf()-nél. • Egyes rendszerek máshogy jelzik a szövegfájlokban a sorok végét ('\n'). Windowson két bájt, CR LF (0x0D 0x0A), Unixokon csak LF (0x0A), a fájlvég karakter pedig 0x1A. 48 65 6C 6C 6F 2C 20 76 69 6C 61 67 21 0D 0A 30 0D 0A 31 0D 0A 1A
Hello, vilag!x0Dx0A 0x0Dx0A 1x0Dx0Ax1A
Bináris fájlok • fp=fopen(nev, "…b") • fread(void *ptr, size_t elem_meret, size_t elem_szam, FILE *a_file); • fwrite(const void *ptr, size_t elem_meret, size_t elem_szam, FILE *a_file);
• Lemezre történő kimenet esetén a folyam a sorlezáró '\n' karaktert "\r\n" karakter párral (CR-LF) helyettesíti. Megfordítva: lemezes bemenetnél a CR-LF karakter párból ismét LF karakter lesz. Ezt nevezzük transzlációnak. • Ez csak szöveges fájloknál megy végbe binárisnál nem.
*p2;
Pozícionálás A folyamokat rendszerint szekvenciális fájlok olvasására, írására használják. A magas szintű bemenet, kimenet a fájlt bájtfolyamnak tekinti, mely a fájl elejétől (0 pozíció) indul és a fájl végéig tart. A fájl utolsó pozíciója a fájlméret-1. Az adatátvitel mindig az aktuális fájlpozíciótól kezdődik, megtörténte után a fájlpozíció a fájlban következő, át nem vitt bájtra mozdul. A fájlpozíciót fájlmutatónak is szokás nevezni.
*p2;
Fájlkezelő függvények • fseek(fp, pozíció, honnan) Ugrás a bájtban megadott pozícióra. A honnan értékei: SEEK_SET=elejétől, SEEK_END=végétől, SEEK_CUR=aktuális pozíciótól számolva, a számozás itt is 0-tól kezdődik. • ftell(fp) Az aktuális pozíció lekérdezése (bájtokban). • fputc(c, fp), fgetc(fp) A putchar() és getchar() párja. • fputs(str, fp), fgets(str, méret, fp) A puts() és a gets() párja. • errno Globális változó, ami a legutolsó hiba kódját tartalmazza.
*p2;
Fájlméret • Írjunk fájlméretet megállapító függvényt! • A fajlmeret elteszi a pillanatnyi pozíciót az aktpoz változóba, hogy a fájl végére állítás után helyre tudja hozni a fájlmutatót. A lekérdezett fájlvég pozíció éppen a fájlméret.
*p2;
#include <stdio.h> long fajlmeret(FILE *stream) { long aktpoz, hossz; aktpoz = ftell(stream); fseek(stream, 0L, SEEK_END); hossz = ftell(stream); fseek(stream, aktpoz, SEEK_SET); return(hossz); }
Getline és scanf megfelelői fájlokra char *fgets(char *s, int n, FILE *stream);
int fscanf(FILE *stream, const char *format<, cim, ...>);
Vegyük észre, hogy a jegyzet eleje óta használt getline függvény csak annyiban tér el az fgets–től, hogy: • A beolvasott karakterlánc méretét adja vissza. • A szabvány bemenetről (stdin) olvas, s nem más folyamból, így eggyel kevesebb a paramétere. • n karaktert hoz be legfeljebb, vagy '\n'-ig, de magát a soremelés karaktert nem teszi be az eredmény karakterláncba.
Az fscanf minden mezőt a format karakterláncnak megfelelően konvertál, es letárol rendre a paraméter címeken. A format karakterláncban ugyanannyi konverziót okozó formátumspecifikációnak kell lennie, mint ahány bemeneti mező van
*p2;
Fájlolvasás - szöveges fájlok
*p2;
#include <stdio.h> #include <stdlib.h>
#include <stdio.h> #include <stdlib.h>
void main() { FILE *f; char c; f=fopen("C:\\temp\\szoveg.txt","rt"); if(f != NULL){ while((c=fgetc(f))!=EOF){ printf("%c ",c); } fclose(f); } getchar(); }
void main() { FILE *f; char szoveg[100]; f=fopen("C:\\temp\\szoveg.txt","rt"); if(f != NULL){ while((fscanf(f, "%s", szoveg))!=EOF){ printf("%s\n", szoveg); } fclose(f); } getchar(); }
Fájlolvasás - szöveges fájlok #include <stdio.h> #include <stdlib.h> #include SORHOSSZ 200 void main() { FILE *f; char sor[SORHOSSZ + 1]; f=fopen("C:\\temp\\szoveg.txt","rt"); if(f != NULL){ while(fgets(sor,SORHOSSZ,f)){ printf("%s ",sor); } fclose(f); } getchar(); }
Karakter beolvasása: c=fgetc(f); Sor beolvasása: fgets(sor, HOSSZ, f); Szöveg beolvasása formázottan: fscanf(f,"%s", szoveg); fscanf(f,"%s %s %d",s1,s2,&szam);
*p2;
Abszolút/relatív útvonal A fájlok elérési útvonala (path) megadható abszolút vagy relatív módon is. Pl: C:\Solution\Debug\Project.exe FILE *relative1; FILE *relative2; FILE *absolute; relative1 = fopen("myfile.txt","r"); relative2 = fopen("data\\myfile.txt","r"); absolute = fopen("c:\\tmp\\myfile.txt","r");
*p2;
Feladat - fájlolvasás
*p2;
• Adott egy szöveges állomány, amelyben angol szavak vannak, minden szó után egy szóköz áll. Írjunk programot, amely képernyőre írja azokat a sorokat (több ilyen is lehet), amelyekben a legkevesebb szó van! • Feltételezhetjük, hogy a feldolgozandó állomány neve és helye c:\temp\szoveg.txt, és az állomány egyetlen sora sem tartalmaz 200-nál több karaktert.
Megoldás: fajlolvasas06kevesebbSzo.c
Feladat – fájlolvasás CSV
*p2;
• Olvasson be tetszőleges szöveges CSV állományt, ennek elérési útvonalát parancssori paraméterekkel lehessen megadni. • Listázza az egyes mezőket rendre egymás utáni sorokban.
• A CSV fájlformátum szemléltetése
Megoldás: fajlolvasas07csv.c
CSV beolvasás kovMezo függvénnyel char* kovMezo(char* sor) { static char* mm = NULL; char *eleje, *vege; char idezet = 0; if (sor) mm = sor; else if (!mm) return NULL; eleje = vege = mm; while (*mm && (*mm!= ELVALASZTO || idezet)) { if (*mm == SZOVEGHAT) if (!idezet) idezet = 1; else if (*(mm + 1) == SZOVEGHAT) *vege++ = *mm++; else idezet = 0; else *vege++ = *mm; mm++; } if (*mm) mm++; else mm = NULL; *vege = '\0'; return eleje; }
SZOVEGHAT » pl.: " vagy ' ELVALASZTO » pl.: ; vagy , Hívása pl.: while (fgets(sor, MAX, f)){ mezo = kovMezo(sor); while (mezo != NULL) { printf("\t%s", mezo); mezo = kovMezo(NULL); } } fclose(f);
*p2;
Feladat – fájlolvasás CSV
*p2;
• Adott egy szöveges csv állomány, pontosvesszővel tagolva. Keresse meg a Jani nevű felhasználót és írja ki a hozzá tartozó email címet. Valósítsa meg ezt egy char* nevKeres(char *f, char *keresendoString) prototípusú függvénnyel. • Feltételezhetjük, hogy a Jani létezik, további feladatként viszont készüljön fel minél több lehetséges hibára. (hosszú sorok, hibás fájl, nem elérhető, nem létező fájl, stb.) Megoldás: nevek02.c nevek01.c
Feladat - szöveges fájl1 írás #include <stdio.h> #include <stdlib.h> void main(){ FILE *f = fopen("c:\\temp\\szoveg.txt", "w"); if (f == NULL){ printf("Hiba\n"); getchar(); } else { fprintf(f, "Szoveg.\nEs meg tobb szoveg."); fclose(f); } }
*p2;
CSV fájlformátum Notepad szinusz1;szinusz2;koszinusz1 0,0500;0,1100;0,9988 0,0998;0,2598;0,9950 0,1494;0,2494;0,9888 0,1987;0,2587;0,9801 0,2474;0,3074;0,9689 0,2955;0,3155;0,9553 0,3429;0,4429;0,9394 0,3894;0,5000;0,9211 0,4350;0,5350;0,9004 0,4794;0,5194;0,8776 0,5227;0,5827;0,8525 Coma Separated Values, szöveg alapú fájl
Excel szinusz1 0,05 0,0998 0,1494 0,1987 0,2474 0,2955 0,3429 0,3894 0,435 0,4794 0,5227
szinusz2 koszinusz1 0,11 0,9988 0,2598 0,995 0,2494 0,9888 0,2587 0,9801 0,3074 0,9689 0,3155 0,9553 0,4429 0,9394 0,5 0,9211 0,535 0,9004 0,5194 0,8776 0,5827 0,8525
Feladat - szöveges fájl2
*p2;
Írjon CSV fájlba szinusz, véletlenszámmal módosított szinusz és koszinusz értékeket. Figyeljen a helyi beállítások használatára (tizedes vessző!)
szinusz1 0,05 0,0998 0,1494 0,1987 0,2474 0,2955 0,3429 0,3894 0,435 0,4794 0,5227
szinusz2 koszinusz1 0,11 0,9988 0,2598 0,995 0,2494 0,9888 0,2587 0,9801 0,3074 0,9689 0,3155 0,9553 0,4429 0,9394 0,5 0,9211 0,535 0,9004 0,5194 0,8776 0,5827 0,8525
1.5 1 0.5 0 1 9 17 25 33 41 49 57 65 73 81 89 97 105113 121129137145153161169177185193 -0.5 -1 -1.5 szinusz1
szinusz2
koszinusz1
Megoldás: fajliras02csv.c
Feladat – szöveges fájl3 • Hozzon létre a felhasználó által megadatott nevű fájlt, majd írjon bele egy sakktábla mintázatot a felhasználó által megadott mérettel.
*p2;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Megoldás: fajliras03csillag.c
Feladat - szövegfájl egyesítés Készítsen programot, mely az indító parancssorban megadott szövegfájlokat egyesíti a megadás sorrendjében a parancssorban utolsóként előírt azonosítójú szövegfájlba! Ha parancssori paraméter nélkül indítják a programot, akkor ismertesse a képernyőn, hogyan kell használni! Ha csak egy fájlazonosító van a parancssorban, akkor a szabvány bemenet másolandó bele. A fájlok egyesítése során a folyamat előrehaladásáról tájékoztatni kell a képernyőn! A szabvány bemenet másolása esetén végül közlendő az eredményfájl mérete!
*p2;
Feladat - szövegfájl Készítsen programot, mely eldönti az indító parancssorban megadott azonosítójú fájlról, hogy ASCII kódú szövegfájl-e, vagy bináris fájl-e! Ha parancssori paraméter nélkül futtatják a programot, akkor ismertesse a képernyőn a használatát!
*p2;
Feladat – szöveges fájl 8
*p2;
• Írjunk programot, amely egy létező szöveges állomány minden sorát 80 karakter hosszúságúra egészíti ki szóközökkel, ha a sorok 80 karakternél rövidebbek, és csonkítja őket a végükön, ha 80 karakternél hosszabbak! Az új sorokat egy új szöveges állományba kell írni.
Megoldás: fajlolvasas08.c
Könyvtárak listázása #include #include #include #include #include #include
<stdio.h> <string.h>
void main(){ char path[50] = "C:\\temp"; struct _finddata_t fajlLista; long hFile; if(mkdir("C:\\temp\\ujMappa")) fprintf(stderr, "Nem hozhato letre a mappa\n"); printf("Listazzuk a %s tartalamt\n\n", path); if(_chdir(path)) fprintf(stderr, "Nem talalom: %s\n", path); else{ hFile = _findfirst("*.*", &fajlLista); while(_findnext(hFile, &fajlLista) == 0){ if(!strcmp(fajlLista.name, "Program Files") || !strcmp(fajlLista.name, "ujMappa")) printf("- %s\n", fajlLista.name); else printf(" %s\n", fajlLista.name); } _findclose(hFile); } getchar(); }
*p2;
Időkezelés
*p2;
• ANSI/ISO szabványos függvények lekérdezik, tárolják, konvertálják, stb. az aktuális (rendszer) időt. Két típusuk: » Az időzónának megfelelő helyi idő. » Egyezményes (UTC) naptári idő, amit korábban greenwichi közép időnek (GMT) neveztek.
• Kezelése: time.h fejfájl. Típusok és típusok közti konverziók. Idők különbségei másodpercben
double 1970 óta eltelt másodpercek
Jelmagyarázat: time
Dátum, idő stringként
ctime
time_t
típusok
függvények
Dátum, idő struktúraként
struct tm*
char *
Időkezelés Több függvény osztozik két statikus élettartamú pufferen. Ezek: • Az idő karakterlánc char * típusú, statikus karaktertömbben. Pontosan 26 karakteres: "Tue Jan 05 12:03:55 1999\n\0" formájú. 24-órás az időszámítás. Minden mező konstans szélességű. Az új sor és a lezáró null karakter mindig a vég. • Az idő struktúra struct tm típusú struktúra, melynek tagjai int–ek. A tagok: » tm_sec: Másodperc (0–59). » tm_min: Perc (0–59). » tm_hour: Óra (0–23). » tm_mday: Nap a hónapon belül (1–31).
*p2;
Időkezelés • Az idő struktúra tagjai még: » tm_mon: Hónap az éven belül (0–11, a január a 0). » tm_year: Év - 1900. » tm_wday: A hét napja (0–6, a vasárnap a 0). » tm_yday: Napszám az éven belül (0–365, a január 1 a zérus). » tm_isdst: Nyári időszámítás jelző, mely pozitív, ha a nyári időszámítás hatályban van, és 0, ha nincs. Negatív a jelző, ha a nyári időszámítás állapota ismeretlen.
*p2;
Időkezelés • A time_t aritmetikai típus (többnyire long). • A time_t time(time_t *sectime); függvény az aktuális rendszer időt 1970. január elseje éjfél óta eltelt másodpercek számában, time_t típusban szolgáltatja. • A double difftime(time_t ido1, time_t ido0); visszaadja az ido1 és az ido0 naptári idők különbségét, azaz a közben eltelt időt másodpercekben.
*p2;
Időkezelés size_t strftime (char *puffer, size_t maxmeret, const char *format, const struct tm *idoptr); size_t wcsftime (wchar_t *puffer, size_t maxmeret, const wchar_t *format, const struct tm *idoptr);
• Az strftime és a wcsftime függvények az idoptr mutatta, struct tm típusú időértéket megformázzák a format vezérlő karakterláncban előírtak szerint, és az eredmény karakterláncot elhelyezik a legfeljebb maxmeret karakteres puffer–ben. A függvények között, hogy a különbség a puffer 1 vagy 2 bájtos karakter-e.
*p2;
Időkezelés struct tm *gmtime (const time_t *ido); struct tm *localtime (const time_t *ido);
• A függvények a paraméter, time_t típusú, GMT (UTC, vagy egyezményes) idő értéket statikus, tm típusú struktúrába konvertálják, és ennek az idő struktúrának a címét visszatérési értékként szolgáltatják. Ha az ido érték 1970. január elseje éjfél előtti, akkor a rutinok NULL mutatót adnak vissza. A localtime korrigálja az időt a helyi időzónára, míg a gmtime ilyet nem tesz.
*p2;
Időkezelés char *ctime (const time_t *ido);
• Az ido mutatta, time_t típusú időt alakítja a helyi időzóna beállításoknak is megfelelő idő karakterlánccá. A fenti függvény elhelyezi a statikus pufferben, és erre mutató mutatót ad vissza.
*p2;
Időkezelés // kiíratás v2 printf("%d.%d.%d (%d. nap)\n", 1900 + when.tm_year, when.tm_mon + 1, when.tm_mday, when.tm_yday + 1); // nap növelése when.tm_mday+=1; mktime(&when);
#include <stdio.h> #include void main(){ int yy, mm, dd; struct tm when = {0}; char buffer[100] = ""; // dátum beolvasása sscanf_s("2015.12.31", "%d.%d.%d", &yy, &mm, &dd); when.tm_year = yy - 1900; when.tm_mon = mm - 1; when.tm_mday = dd; mktime(&when); // kiíratás v1 strftime(buffer,80,"%Y %b %d %I:%M%p -%c\n", &when); printf(buffer);
//kiíratás újra printf("%d.%d.%d (%d. nap)\n", 1900 + when.tm_year, when.tm_mon + 1, when.tm_mday, when.tm_yday + 1); getchar(); }
2015 Dec 31 12:00AM -- 12/31/14 00:00:00 2015.12.31 (365. nap) 2016.1.1 (1. nap)
*p2;
Várakozás [ms] • #include <Windows.h> //így OS függő lesz Sleep(900); //900 ms = 0,9 másodperc • #include <stdlib.h> _sleep(900); //obsolete – elavult
• Más nyelveken általában delay, wait vagy sleep esetleg pause.
*p2;
Várakozás [sec] #include void sleep(clock_t sec) { clock_t ig = sec*CLOCKS_PER_SEC + clock(); while (ig > clock()); } • A clock_t clock(); függvény a hívó folyamat által elhasznált processzor időt szolgáltatja, a clock_t aritmetikai típus (többnyire long). • A clock által visszaadott értékből úgy kapunk másodpercet, hogy elosztjuk a CLOCKS_PER_SEC szimbolikus állandóval. • Tehát a fenti függvény sec másodpercet vár.
*p2;
Feladat
*p2;
• Nézzük az idok.c példaprogramot, amely illusztrálja jó néhány időkezelő függvény használatát. 1970. januar 1. ota eltelt masodpercek szama: 1460891778 A UNIX ido es datum: Sun Apr 17 13:16:18 2016 Az egyezmenyes ido: Sun Apr 17 11:16:18 2016 12-oras ido: 01:16:18 du Karacsony Fri Dec 25 12:00:00 2020 Karacsony + 10 nap: Mon Jan 4 12:00:00 2021 2021. January 4. Monday van a(z) Romance Standard Time idozonaban. 2021. január 4. hétfo van a(z) Romance Standard Time idozonaban.
Megoldás: idok.c
Változó paraméterlista • Néhány fv. hívásonként eltérő számú paramétert fogad, pl.: int printf(const char *format, ...);
• stdarg.h typedef #define #define #define #define
char *va_list; _INTSIZEOF(x) ((sizeof(x)+sizeof(int)-1)&~(sizeof(int) -1)) va_start(ap, utsofix) (ap=(va_list)&utsofix+_INTSIZEOF(utsofix)) va_arg(ap, tipus) (*(tipus *)((ap+=_INTSIZEOF(tipus)) - _INTSIZEOF(tipus))) va_end(ap) ap = (va_list)0
*p2;
Változó paraméterlista #include <stdio.h> #include <stdarg.h> void sum(char *uzen, ...) { int osszeg = 0, tag; va_list param; va_start(param, uzen); while(tag = va_arg(param, int)) osszeg += tag; va_end(param); printf(uzen, osszeg); } void main() { sum("1+2+3+4 = %d\n", 1, 2, 3, 4, 0); getchar(); }
*p2;
Feladat – változó paraméterlista 1
*p2;
• Írjunk egy változó paraméterszámú függvényt, amely összefűz tetszőleges számú sztringet, és az így kapott új sztringgel tér vissza! A függvény első paramétere az összefűzendő sztringek darabszáma, további paraméterei pedig maguk a sztringek. • konkat(5, "Ez ", "egy ", "jó ", "hosszú ", "sztring!");
Megoldás: valtozoparam01.c
Feladat – változó paraméterlista 2
*p2;
• Írjunk egy változó paraméterszámú függvényt, amely összefűz tetszőleges számú sztringet, és az így kapott új sztringgel tér vissza! A függvény legalább egy sztringet vár paraméterként, utolsó paraméterének a NULL pointernek kell lennie, ezzel jelezzük a paraméterlista végét. • konkat("Ez ", "egy ", "jó ", "hosszú ", "sztring", NULL);
Megoldás: valtozoparam02.c
Numerikus integrálás húrtrapézformulával • A trapéz szabály egy közelítő numerikus eljárás a határozott integrál meghatározására 𝑏
𝑓 𝑏 + 𝑓(𝑎) න 𝑓 𝑥 𝑑𝑥 ≈ (𝑏 − 𝑎) 2 𝑎
*p2;
Numerikus integrálás húrtrapézformulával 𝑖+1
න 𝑖
𝑥[𝑖] + 𝑥[𝑖 + 1] 𝑓 𝑥 𝑑𝑥 ≈ ∆𝑇 2
dT * (x[i] + x[i+1]) / 2.0
*p2;
Numerikus integrálás húrtrapézformulával 𝑖+1
න 𝑖
𝑥[𝑖] + 𝑥[𝑖 + 1] 𝑓 𝑥 𝑑𝑥 ≈ ∆𝑇 2
// x - az integrálandó tömb // xSize - x mérete // dT - delta T a mintavételezés időköze double numIntegralTrapez(double *x, int xSize, double dT){ int i; double result = 0; for(i = 0; i < xSize - 1; i++) result += (dT * (x[i] + x[i+1]) / 2.0); return result; }
*p2;
Feladat - Szenergy • A csatolt szenergy_2014_10_18_17_18_murcia.csv és szenergy_2014_10_17_16_06_rotterdam.csv táblázatos formában tartalmazza egyetemünk Szenergy hallgatói csapatának telemetriás adatait egy spanyol és egy holland versenyről. • Feladatunk a rendelkezésekre álló adatokból a körönként megtett távolság kiszámítása. Mivel a fájl tartalmazza a körök számát is az értékekhez, így nem csak a verseny, de a körönkénti távolság is kiszámítható. Ezek közel azonos érték kellenek, hogy legyenek, de előzések és a pályán belüli eltérő nyomvonalak okozhatnak némi eltérést. • Figyeljünk rá, hogy csak m/s-ből tudunk métert számolni. for (i = 0; i < recordNumber; i++) pSpeedMps[i] = pSpeedKmph[i] / 3.6;
Megoldás: szenergy00.c szenergy01.c szenergy02.c
*p2;
Lineáris és Lagrange interpoláció Lineáris 35 30 25
interpolált
20
alappontok
15 10 5 0 1
1.2
1.4
1.6
1.8
2
2.2
2.4
2.6
2.8
3
3.2
3.4
3.6
3.8
4
4.2
4.4
4.6
4.8
5
4
4.2
4.4
4.6
4.8
5
Lagrange 30 25 20 interpolált
15
alappontok
10 5 0
1
1.2
1.4
1.6
1.8
2
2.2
2.4
2.6
2.8
3
3.2
3.4
3.6
3.8
*p2;
Feladat - interpoláció
*p2;
• Vegyük a példaként is említett alappont-érték párosokat. 𝑥0 𝑥1 𝑥2 𝑥3
= = = =
1; 𝑦0 2; 𝑦1 3,3; 𝑦2 5; 𝑦3
= 10,3 = 18,956 = 12 = 30
• Írjunk programot, amely a standard kimenetre kiírja 0,1-es lépésközzel az interpoláció értékeit mind Lagrange, mind lineáris módszerrel. Plusz feladatként írjuk csv fájlba az eredményt. Megoldás: interpolacio01.c
Lineáris és Lagrange interpoláció
*p2;
// *x - az interpolációs alappontok helye // *y - az interpolációs alappontok értéke // n - az interpolációs alappontok darabszáma (*y és *y tömb int lagrangeInterp(double *x, double *y, int n, double x1, double *y1){ elemszáma) double p = 0.0, s; // x1 - a hely, ahol meg szeretnek kapni *y1 érteket int i, j; int linInterp(double *x, double *y, int n, double x1, double *y1){ for (i = 0; i < n; i++){ int i; s = 1.0; for (i = 0; i < n - 1; i++){ for (j = 0; j < n; j++){ if ((x[i] <= x1) && (x[i + 1] >= x1)){ if (i != j){ *y1 = y[i] + (y[i + 1] - y[i]) / (x[i + 1] - x[i])*(x1 - x[i]); if (x[i] == x[j]) return 1; return 0; else s = s*(x1 - x[j]) / (x[i] - x[j]); } } } } return 1; p += y[i] * s; } } *y1 = p; return 0; }
Header fájlok • Kódfájlok (.c) függvények definíciói • Fejlécfájlok (.h) mennek a függvények deklarációi #include <stdio.h> #include "sajat.h" » A két megadás között a különbség annyi, hogy más mappában keresi majd a fordító.
*p2;
Header fájlok
*p2;
Az alábbi példában is a kódfájlokban (.c) a függvények definíciói, a fejlécfájlokban pedig (.h) a függvények deklarációi kaptak helyet. main.c #include <stdio.h> #include "oraifuggvenyek.h" void main(){ char s[21]; getline(s, 20); printf(s); }
oraifuggvenyek.h
int getline(char *s, int n);
oraifuggvenyek.c opcionális
#include <stdio.h> #include "oraifuggvenyek.h" int getline(char *s, int n){ int c; char *t = s; while (n-- > 0 && (c = getchar()) != EOF&&c != '\n') *t++ = c; *t = '\0'; while (c != EOF&&c != '\n') c = getchar(); return(t - s); }
Feladat - autószerviz
*p2;
• Egy autószerviz számítógépe minden megbízásról egy-egy sort ír a SZERVIZ.TXT fájlba. Egy sor a következő adatokat tartalmazza pontosvesszővel elválasztva: RENDSZÁM : TULAJDONOS NEVE : FORG. ENG. SZÁMA : DÁTUM : ÖSSZEG :
6 40 8 11 6
karakter karakter (legfeljebb 40) karakter karakter (2013.02.19.) karakter (legfeljebb 6 karakter, egész szám)
• Írjon C programot, amely a SZERVIZ.TXT fájl alapján névsorban kiírja azoknak az autótulajdonosoknak a nevét, akik az átlagosnál többször javíttatták 2013 februárjában autójukat. A fájlt csak egyszer olvashatja be! • Használjon dinamikus adat szerkezetet! Megoldás: szerviz.c
Feladat - email címek • Írjon egy olyan szabványos ANSI C programot, amely parancssori paraméterként kapja két szövegfájl nevet! Az első fájl egy weboldalt leíró html szöveg. • A program gyűjtse ki a fájlban található e-mail címeket, melyeket aztán lista adatszerkezetben taroljon (egy címet elég egyszer eltarolni)! Az e-mail címek olyan karaktersorozatok, melyek az angol ABC kis- es nagybetűiből, számokból, pontokból (.), kötőjelekből(-) es aláhúzás (_) jelekből állnak, es kötelezően tartalmaznak pontosan egy @ karaktert. Az email címben nem állhat egymás mellett két pont, valamint nem állhat egymás mellett egy pont es egy @. Minden egyéb, itt fel nem sorolt karakter elválasztó karakternek minősül. Egy e-mail cím hossza maximum 100 karakter. Ha ennél hosszabb, amúgy érvényes karaktersorozatra bukkanunk, azt ne tekintsük email címnek! A nem e-mail cím karaktersorozatok hossza bármekkora lehet, ezek fix hosszúságú tömbben való eltarolása súlyos hibának minősül. • A második parancssori paraméterként kapott szövegfájl minden sora egy-egy, korábban kigyűjtött e-mail címet tartalmaz. A program olvassa be az e-mail címeket, es fésülje össze a most talált címekkel, majd mentse el ugyanebbe a fájlba az összes címet abc sorrendben, minden címet csak egyszer taroljon!
*p2;
_CRT_SECURE_NO_WARNINGS Project » Properties » Configuration properties » C/C++ » Prepocessor _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS
Amennyiben a következő error fogad, akkor célszeű használni: "This function or variable may be unsafe."
*p2;
Felhasznált irodalom • A C programozási nyelv - Az ANSI szerinti változat. B. W. Kernighan - D. M. Ritchie; Műszaki Könyvkiadó, 1995 • Programozás C nyelven - Pere László, Kiskapu 2003 • Programozási feladatok és algoritmusok Turbo C és C++ nyelven Benkő Tiborné, Benkő László, ComputerBooks 1997 • A programozás alapjai - Pohl László, Budapest, 2010 • Programozás I-II. C programnyelv - Bauer Péter, UniversitasGyőr Kht., 2005
*p2;