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
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 2
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).
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
4
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 5
Programozási nyelvek népszerűsége (2012) C
Java
Objective-C
C++
C#
Egyéb
19%
39%
16%
7%
Forrás: http://www.tiobe.com
10% 9%
6
1987-től 2012-ig C Java Objective-C C++ C# PHP (Visual) Basic Python Perl Ruby Lisp Ada
2012 1 2 3 4 5 6 7 8 9 10 13 18
2007 2 1 43 5 7 4 3 8 6 10 16 19
1997 1 5 2 3 29 7 10 16
1987 1 6 5 3 2
7
C történelem • • • • •
1970 B - Bell Labs, Ken Thompson 1972 C - Bell Labs, Dennis Ritchie 1973 UNIX kernel C-ben átírva 1978 Brian Kernighan & Ritchie: The C Programming Language 1990 ANSI C
Thompson
Ritchie
Kernighan
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
K meghajtó \\x346-1.eik.sze.hu\szakkabinet\kozos\GB_IN001_2_programozas_2
ANSI C adattípusok Típus
Méret bájtban (minimum)
long
1 1 2 2 4
unsigned long
4
char unsigned char
int unsigned int
Méret bitben Határok: (minimum) Egész 8 -128
–
127
8
0
–
255
16
-32768
–
32767
16
0
–
65535
32
-2.147.483.647
-
2.147.483.647
32
0
–
4.294.967.295
-
±3.4*10+38
Lebegőpontos float
4
32
±3.4*10-38
6–7 decimális jegy pontosság double
8
32
±1.7*10-308
-
±1.7*10+308
15–16 decimális jegy pontosság long double
10
80
±1.2*10-4932
-
±1.2*10+4932
19 decimális jegypontosság
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
Tömör C kód mutatókkal #include <stdio.h>
main(t,_,a)char *a;{return!0
q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# \ ){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' \ iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c \ ;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# \ }'+}##(!!/")
:t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1) :0
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
Csere mutatókkal void csere(int *px, int *py){
Main:
int tmp;
int a=22, b=1020;
tmp = *px;
csere(&a, &b);
*px = *py;
printf("%d", b);
*py = tmp; } //a módosítandó értékek címének átadása
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");
Cím szerinti paraméter átadás void ertekSzerint(char x){ x = 22; } void cimSzerint(char *x){ *x = 33; } void main(){
Futás: Ertek szerint: 1 Cim szerint: 33
char e = 1, c = 1; ertekSzerint(e); cimSzerint(&c); printf("Ertek szerint: %d\nCim szerint: %d", e, c); }
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]
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.
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*))
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.
int getline(char s[],int lim){ int c, i;
for(i=0; i
s[i]='\0'; while(c!=EOF && c!='\n') getchar(); return(i); }
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)
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); }
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';
}
*squeeze • char *squeeze(char *s, int c) • c karakter törlése az s karakterláncból a saját helyén.
*squeeze main(){ char s[]="Ebben vannak n-ek"; printf("%s\n",squeeze(s,'n')); return(0); }
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;
}
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); }
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); }
atoi • • • •
Eredeti: stdlib.h Karakterlánc egésszé alakítása (isspace >> ctype.h) Íjuk meg ennek is a mutatós változatát!
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)
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; } }
strrev 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
2
3
4
x
6
7
8
a
'\0'
[i=1]
9
8
3
4
x
6
7
b
a
'\0'
[i=2]
9
8
7
4
x
6
c
b
a
'\0'
[i=3]
9
8
7
6
x
d
c
b
a
'\0'
void main(void){ int i,n; float array[SIZE]; printf("\nValos szamok tombbe olvasasa es visszairasa cimeikkel egyutt.\n"); printf("Legfeljebb %d szám van, ill. EOF-fal kevesebb!\n", SIZE); printf("Adja meg a szamokat!\n\n"); // A bekérések sorszámozása:
for(n=0; n<SIZE; ++n) if(printf("%4d: ", n+1), (i=getfloat(array+n))==EOF) break;
else if(!i) --n; printf("\n\nA tombelemek cimei rendre:\n\n"); for(i=0;i
printf("\n\nA tombelemek ertekei rendre:\n\n"); for(i=0;i
getchar(); }
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);
Mutatótömbök • Mutatókat tartalmazó tömbök • Pl. stringeket is tárolhatunk így tömbben • A main() argumentumlistája
#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]="Valami"; 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);}
Függvény (kód) mutató • 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);
*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ó.
A printf utolsó, *argv++ kifejezése 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.
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.
Dinamikus memóriakezelés A C fordító a memóriát három részre osztja: 1. data segment » elsődleges adatterület » globális objektumok, konstansok 2. stack segment » verem adatterület » lokális objektumok, függvények paraméterei 3. heap segment » dinamikus adatterület » dinamikusan, futásidőben foglalt, változó méretű memóriablokkok Az stdlib.h fejfájl dinamikus memóriakezelést megavlósító függvényei: void *malloc(m), void *calloc(a,b), void free(void *memória), void realloc(c,m) stb.
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.
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.
memcpy, memccpy • void memcpy(void *cél, const void *forrás, size_t darab);
a memóriaterület tartalmát másolhatjuk át
• void memccpy(void *cél, const void *forrás, int keres, size_t darab);
a memóriaterület tartalmát másolhatjuk át, adott méret vagy hatójel alapján
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!
Feladat memóriafoglalás malloc1.c • Két karakterlánc egymás után másolása az általa lefoglalt memóriaterületre • A __func__ helyére a fordító a függvény nevét jelző mutatót helyezi. #ifndef __func__ #define __func__ __FUNCTION__ #endif » Nem ANSI C szabványos megoldás.
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.
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ó! }
Feladat mátrixszozás 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!
2d tömb indexelése 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
2D tömb megvalósítások 1.
Sorfolytonosan
2. Soronkénti foglalás
3. Leképezés és mutatótömb "jagged array"
Soronkénti foglalás egydimenziós mutatótömbbel #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]); putchar('\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!
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(); }
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;
Feladat Sudoku generálás 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ő.
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
Typedef #include <stdio.h> typedef int egesz;
void main(){ egesz e=42; printf("e=%d\n", e); }
Az egesz típus közvetlenül az int típusból származik. Persze ez így még nem túl hasznos.
Struktúrák #include <stdio.h> struct Pont{ int x; int y;
int z; }; void main(){ struct Pont p1, p2; p1.x=8; p1.y=5; }
A ; nem véletlen a struktúrák után.
Typedef #include <stdio.h> struct Pont{ int x; int y; int z;
}; typedef struct Pont pont;
A Pont egy struktúra, a pont pedig típusdefiníció. A ; nem véletlen a struktúrák után.
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); }
typedef struct Pont { int x; int y; Elhagyható – int z; névtelen } pont; struktúra.
Typedef 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;
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.
Feladat Í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!
typedef struct{ int x; int y; }Pont; //szakasz két pont közt typedef struct{ Pont eleje; Pont vege; }Szakasz;
Megoldás: szakasz.c
Feladat Í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 Barat{ char nev[32]; int baratja; } barat; barat baratok[10];
int kolcsonos(int szemely){ if(baratok[baratok[szemely].baratja].baratja==szemely) return 1; else return 0; } Megoldás: szakasz.c
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
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
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
’/’ 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
'*'
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.
Feladat - Dinamikus tömb 02 Í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; } Pont; Pont *sokszog;
A struktúra méretét megadja az n*sizeof(Pont) kifejezés. sokszog=(Pont*)malloc(n*sizeof(Pont)); Megoldás: dintomb02sokszog.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: dintom03valogat.c
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;
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
Egyszerű láncolt lista typedef struct elem { int i; struct elem *kov; } elem_t;
elem_t *kezd; kezd = malloc(sizeof(elem_t)); kezd->i = 35; kezd->kov = malloc(sizeof(elem_t)); kezd->kov->i = 88;
A lista bejárása ListaElem *mozgo; for (mozgo=eleje; mozgo!=NULL; mozgo=mozgo->kov){
printf("%d", mozgo->szam); } Nézzünk erre egy példát! lancolt00alap.c
Forrás: A programozás alapjai - Pohl László, Budapest, 2010
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);
Beszúrás a lista elejére //lista elejére tesz számokat Lista *listaElejere(Lista *eleje, int adat) { Lista *uj=(Lista*)malloc(sizeof(Lista)); uj->kov = eleje; uj->adat = adat; eleje = uj; return eleje; }
Beszúrás a lista végé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" é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);
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; }
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. • 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
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
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.
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);
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"); • A következő, két setlocale hívás funkcionálisan ekvivalens: setlocale(LC_ALL, "English"); setlocale(LC_ALL, "English_United States.1252");
Helyi beállítások //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");
Megoldás: helyi01.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.
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!");
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 sikeresetén EOF-ot, ha nem sikerült lezárni
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 ...
ec 77 c5 ہ
Szöveges fájlok • 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.
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.
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.
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.
#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
Fájlolvasás - szöveges fájlok #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);
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");
Feladat - fájlolvasás • Adott egy szöveges állomány, amelyben angol szavak vannak, minden szó után egy szóköz áll. Írjunk eljárást, 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 2000-nél több karaktert.
Megoldás: fajlolvasas06kevesebbSzo.c
Feladat – fájlolvasás csv • 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* searchName(char *fileName, char *stringToFind) 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, 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); } }
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 Í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.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
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!
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!
Feladat - függvénymutató • Í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))
Időkezelés • 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.
• time.h fejfájl. Típusok és típusok közti konverziók.
Időkezelés Több fv. 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).
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.
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 újra printf("%d.%d.%d (%d. nap)\n", 1900 + when.tm_year, when.tm_mon + 1, when.tm_mday, when.tm_yday + 1); getchar(); }
// kiíratás v1 strftime(buffer,80,"%Y %b %d %I:%M%p -%c\n", &when); printf(buffer);
2015 Dec 31 12:00AM -- 12/31/14 00:00:00 2015.12.31 (365. nap) 2016.1.1 (1. nap)
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
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(); }
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
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(); }
Feladat – változó paraméterlista 1 • Í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 • Í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
Feladat – szöveges fájl 8 • Í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
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
Numerikus integrálás húrtrapézformulával 𝑖+1 𝑖
𝑥[𝑖] + 𝑥[𝑖 + 1] 𝑓 𝑥 𝑑𝑥 ≈ ∆𝑇 2
dT * (x[i] + x[i+1]) / 2.0
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; }
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
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
Feladat - interpoláció • 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ó // *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; }
Feladat - autószerviz • 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!
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