Programozás I. Széchenyi István Egyetem, Gy r 2013. szeptember 29.
M veletek & kifejezések M veletek az operandusok száma szerint: Egyoperandusos, operátor operandus, pl. 6, sizeof(long) Kétoperandusos, operandus1 operátor operandus2, pl. a*b Háromoperandusos,?:, pl. (a>b)?a:b Kifejezések: operátorok és operandusok (ún. els dleges kifejezések) sorozata, ami értéket számít ki, objektumot/függvényt ér el, mellékhatást generál (kif. kiértékelése közben változó(k) értéke módosul), illetve ezek kombinációját. Operandus lehet: azonosító (balérték, fv. hívás), konstans, karakterlánc, hozzárendelés kifejezés(ek)
Implicit típuskonverzió Kétoperandusos m veleteknél, ha az operandusok típusa eltér. Ált. a pontosabb típusra alakít. Szabályok: egyik operandus long double double float egész-el léptetés unsigned long long (unsigned) long long unsigned int int másik operandus bármi long double bármi double bármi float egész-el léptetés bármi unsigned long unsigned int (unsigned) long bármi long bármi unsigned int int
Egész-el léptetés (integral promotion) Régi típus Új típus Átalakítási módszer char int Alapértelmezett (signed/unsigned) char típustól függ en. unsigned char int Fels bájtok feltöltése zérus bitekkel. signed char int El jel kiterjesztése a fels bájtokra. short int int El jel kiterjesztés. unsigned short unsigned int Feltöltés zérus bitekkel. enum int Változatlan érték. Figyelem! A konverzió id igényes! Karakterlánc sosem alakul aritmetikai értékké!
Aritmetikai m veletek Egyoperandusos operátorok + egész el léptetéssel jár megel zheti implicit típuskonverzió Multiplikatív operátorok (*, / és %) Csoportosítás: balról jobbra Operandusok típusa: aritmetikai (%-nál: egész) Eltér típusok esetén implicit típuskonverzió, az eredmény is az új típusban áll el értékvesztés veszélye (túl-, és alulcsordulás) Nullával nem lehet osztani (még mindig :) Ha az osztások operandusai egészek, de a hányados nem: / törtrész levágása % eredmény az els operandus el jelét örökli
Aritmetikai m veletek Additív operátorok (+, ) implicit típuskonverzió lehetséges eredmény a közös típusban értékvesztés bekövetkezhet (túl- és alulcsordulás) Matematikai függvények ANSI szabvány szerinti függvénykönyvtárak hordozhatóság math.h fejfájl paramétereik típusa és visszatérési értékük többnyire double trigonometrikus fv.-ek paramétere és visszatérési értéke radiánban értend
Aritmetikai m veletek Néhány gyakran használt matematikai függvény Prototípus double ceil(double x) double cos(double x) double cosh(double x) double exp(double x) double fabs(double x) double fmod(double x, double y) double log(double x) double log10(double x) double pow(double x, double y) double sqrt(double x) Funkció Az x-nél nagyobb egészek közül a legkisebbet adja koszinusz hiperbolikus koszinusz exponenciális fv. abszolút érték osztás lebeg pontos maradékát adja természetes alapú logaritmus 10-es alapú logaritmus hatványozás négyzetgyökvonás Az ISO szabvány (draft) szerinti függvények teljes listája is elérhet (ld. 7.12 fejezet)
Aritmetikai m veletek Hiba jelzése: 1 speciális érték (pl. nulla, HUGE_VAL (=legnagyobb ábrázolható double érték), stb.) visszaadásával 2 extern int errno; Hibakódok: errno.h EDOM értelmezési tartomány hiba ERANGE értékkészlet hiba (eredmény nem ábrázolható double típussal) Néhány konstans (math.h) M_E Euler-konstans M_PI π értéke M_SQRT2 2
Relációs és logikai operátorok Relációs operátorok tulajdonságai: Két prioritási szint: <, <=, >, >= ==,!= operandusa lehet egész, lebeg pontos vagy mutató eredmény int típusú (logikai, 0 vagy 1) kifejezés!=0 kifejezés Logikai m veletek! nem; kifejezés==0!kifejezés && és vagy L. m. tulajdonságai: int típusban 0-t vagy 1-et adnak nincs implicit típuskonverzió amit lehet, nem értékel ki a fordító mit melyik operandusba írjunk?
Hozzárendelési konverzió Hozzárendelend érték típusának átalakítása a fogadó típusára. Szabályok: Ld. implicit típuskonverzió szabályai Átalakítás signed egész típusokról nem negatívról legalább ekkora unsigned-ra: érték változatlan, el jelet jelz bit funkcióvesztése negatívról legalább ekkora unsigned-ra: el jel kiterjesztés, majd el jelet jelz bit funkcióvesztése (pl. signed char long unsigned long) hosszabb egészr l rövidebbre: fels bitek levágásával értékvesztés veszélye lebeg pontos típusra: long-ra alakítás után (ha szükséges), pontosságvesztés veszélye
Hozzárendelési konverzió Átalakítás unsigned egész típusokról hosszabb signed/unsigned-ra: fels bitek feltöltése nullákkal rövidebb unsigned-ra: fels bitek levágásával értékvesztés veszélye rövidebb signed-ra: mint el bb, de legmagasabb helyiérték bit már az el jelet jelzi lebeg pontos típusra: long-ra alakítás után, pontosságvesztés veszélye Átalakítás lebeg pontos típusokról rövidebbr l hosszabb lebeg pontosra: érték változatlan hosszabbról rövidebb lebeg pontosra: pontosságvesztés veszélye egész típusokra: törtrész levágásával ábrázolási korláton kívülre es érték: deniálatlan m ködés
Hozzárendelési konverzió Átalakítás egyéb típusokról struktúrák és uniók között nincs konverzió (void)kifejezés érték eldobása void (=nem létez érték) nem alakítható át, hozzárendeléssel más típus sem lehet void-dá.
Explicit típusmódosítás (típus)el tag-kifejezés az értéket alakítja át! követi a hozzárendelési konverzió szabályait lehetséges módosítások: aritmetikai típusok és mutatók egész aritmetikai típusok lebeg pontos bármi void, de fordítva nem megy! explicit típusmódosítás eredménye nem lehet balérték hozzárendelésben
sizeof operátor tároláshoz szükséges memória, bájtban mérve, size_t típusban (ld. stddef.h, ált. unsigned int) sizeof(egyoperandusos-kifejezés) vagy sizeof(típusnév) Pl. tömb elemszáma: sizeof(tomb)/sizeof(tomb[0]) Nem használható: függvényre nem teljes típusú kifejezésre bitmez t kijelöl balértékre De használható: el feldolgozó direktívában, pl. #define MERET sizeof(int)*4
Növelés (++), csökkentés ( ), mellékhatás magas prioritás, el tag és utótag forma, kiértékelési sorrend operandusa skalár típusú, az eredmény nem balérték Minden hozzárendelésnek, és a növel, csökkent operátoroknak is van mellékhatása! c karakter minden el fordulásának törlése az s karakterláncból, ld. típusok és konstansok fejezet 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'; }
Bit szint operátorok Prioritás szerinti sorrendben: ~ egyes komplemens képzés eltolás balra, eltolás jobbra & és ^ kizáró vagy (megenged ) vagy Csak egész típusú adatokkal használhatóak!
Egyes komplemens képzés egész el léptetést végez, ha szükséges eredmény típusa a konvertált típus Példa (32 bites int-et feltételezve) #include<stdio.h> int main(void) { unsigned char c = 0xA; /* 1010 */ printf("%x\n",~c); /* kimenet: fffffff5 azaz 1111 1111 1111 1111 1111 1111 1111 0101 */ return 0; }
Eltolás (shift) op1<<op2 és op1>>op2 op1 bitjeinek eltolása op2 pozícióval (jobbról 0 bitekkel, balról op1 el jel-kezelését l függ en 0 vagy az el jelet jelz bit értékével töltenek fel) operandusok egész típusúak egész el léptetést végez, ha szükséges eredmény típusa op1 konvertált típusa ha op2<0 vagy op2 op1 bitszélessége deniálatlan eredmény ha nem okoz túlcsordulást, akkor op1 << op2 op1 2 op2 op1 >> op2 op1/2 op2 egész része
Eltolás (shift) Példa (32 bites int-et feltételezve) #include<stdio.h> int main(void) { /* signed char c = -0x56; */ /* 0xAA == 1010 1010 * ~0xAA == 0101 0101 * ~0xAA+1 == 0101 0110 == 0x56 */ signed char c = (signed char)0xaa; printf("%x\n",c>>4); /* kimenet: fffffffa */ return 0; }
Bit szint és (&, and), kizáró vagy (^, xor), vagy (, or) implicit típuskonverzió, ha szükséges eredmény a konvertált típusban Figyelem! Ha a==1 és b==2 akkor a&&b==1, de a&b==0 op1 op2 op1&op2 op1^op2 op1 op2 0 0 0 0 0 0 1 0 1 1 1 0 0 1 1 1 1 1 0 1 Bitek törlése 0111 1110 &1100 0011 --------- 0100 0010 Op. lenullázása 0101 0101 ^0101 0101 --------- 0000 0000 Bitek beállítása 0011 1100 1100 0101 --------- 1111 1101
Példa: bájt bináris tartalmának kijelzése pelda14.c, 1. rész /* PELDA14.C -- Byte tartalma binárisan */ #include <stdio.h> #include <stdlib.h> int getline(char s[], int lim); unsigned getbits(unsigned, unsigned, unsigned); void main(void) { int szam = 0; /* A konvertált szám */ int len = 0; /* A beolvasott karakterlánc hossza */ int i; /* Ciklus változó */ char inp[10]; /* Input puffer */ printf("egy bájt bináris tartalmának megállapítása.\n"); while(len<1 szam<0 szam>255) { printf("\ngépeljen be egy 0 és 255 közötti számot!\n"); len = getline(inp, 3); szam = atoi(inp); } printf("\na byte tartalma decimálisan: %3d binárisan: ",szam); for(i=7; i>=0; i--) if(getbits(szam, i, 1) == 1) printf("1"); else printf("0"); printf("\n"); }
Példa: bájt bináris tartalmának kijelzése pelda14.c, 2. rész int getline(char s[], int lim) { /* Max. lim méret karakterlánc beolvasása s-be. A függvény a karakterlánc hosszát adja vissza. s tömbnek lim+1 méret nek kell lennie. */ int c, i; for(i=0; i<lim && (c=getchar())!=eof && c!='\n'; ++i) s[i]=c; s[i] = '\0'; while(c!=eof && c!='\n') c=getchar(); return(i); } unsigned getbits(unsigned x, unsigned p, unsigned n) { /* x n bitje a p-dik poziciótól kezdve. */ return((x>>(p+1-n))&~(~0<<n)); }
Feltételes kifejezés op1?op2:op3 op1 egész, lebeg pontos vagy mutató op1-et zérushoz hasonlítja eredmény típusa op2 és op3-tól függ (csak az egyiket számítja ki) ha mindkett aritmetikai típus esetleges implicit típuskonverzió után a konvertált típus ha ugyanolyan stuktúra/unió/mutató, akkor ez a közös típus ha mindkett void, az eredmény is az 1. megoldás if(a>b) max = a; else max = b; 2. megoldás max = a>b?a:b;
Hozzárendelés operátorok = (egyszer h.), *=, /=, %=, +=, -=, &=, ^=, =, <<=, >>= (összetett v. kombinált h.) op1 = op1 operátor op2 op1 operátor= op2 két oldalán: balérték (tehát nem lehet tömb, függvény, konstans vagy nem teljes típusú) jobbérték (összetett h.: operandusok ált. egész és lebeg pontos típusúak, de += és -= esetén op1 mutató is lehet, ekkor op2 csak egész lehet) hozzárendelés kif. értéke nem balérték hozzárendelési konverzió mellékhatás generálása többszörös hozzárendelés
Példa: 1 érték bitek megszámolása pelda15.c, 1. rész /* PELDA15.C -- Szó 1-es bitjeinek leszámlálása */ #include <stdio.h> #include <stdlib.h> int getline(char s[], int lim); int bitcount(unsigned); void main(void) { int szam = -1; /* A megadott szám */ int len = 0; /* A beolvasott karakterlánc hossza */ char inp[10]; /* Input puffer */ printf("szó 1-es bitjeinek leszámlálása.\n"); while(len<1 szam<0) { printf("\ngépeljen be egy 0 és 32767 közötti számot!\n"); len = getline(inp, 5); szam = atoi(inp); } printf("\n%d számban %d darab 1-es bit van.\n", szam, bitcount(szam)); }
Példa: 1 érték bitek megszámolása pelda15.c, 2. rész int getline(char s[], int lim) { /* Sor beolvasása s-be. A hosszat adja vissza.*/ int c, i; for(i=0; i<lim && (c=getchar())!=eof && c!='\n'; ++i) s[i]=c; s[i]='\0'; while(c!=eof && c!='\n') c=getchar(); return(i); } int bitcount(unsigned n) { /* 1-es bitek leszámlálása az n paraméterben Visszaadott érték az 1-es bitek száma */ int b; for(b=0; n!=0; n>>=1) b+=(n&1); return(b); }
Vessz operátor op1, op2, op3,..., opn balról jobbra csoportosít, mellékhatások megvalósulnak konstrukció típusa és értéke: legjobboldalibb operandus típusa és értéke Ha a vessz más jelentés (ld. inicializátor-lista), zárójelezni kell! Példa: három paramétert fogadó fv. hívása fv(a, (b=1, c+=2), 3); Példa: karakterlánc megfordítása a saját helyén #include <string.h> void strrv(char s[]) { int i, j, temp; for(i=0, j=strlen(s)-1; i<j; ++i, --j) { temp = s[i]; s[i] = s[j]; s[j] = temp; } }
Vessz operátor Elöl- és hátultesztel ciklusok #include <stdio.h> int main(void) { int k; printf("gépeljen be egy számjegy karaktert!\n"); /* do{ k=getchar()-'0'; } while(k<0 k>9); */ while(k=getchar()-'0', k<0 k>9); printf("a beolvasott karakter: %d\n", k); return 0; }
M veletek precedenciája (prioritása, rend sége) Operátor Asszociativitás () (fv. hívás) [ ] (tömb indexelés) ->. (struktúra tag el.) balról jobbra! ~ ++ + - (el jelek) * & (címe operátor) (típus) sizeof jobbról balra * / % balról jobbra + - (összeadás, kivonás) balról jobbra << >> balról jobbra < <= > >= balról jobbra ==!= balról jobbra & (bitenkénti és) balról jobbra ^(bitenkénti kizáró vagy) balról jobbra (bitenkénti vagy) balról jobbra && (logikai és) balról jobbra (logikai vagy) balról jobbra?: (feltételes kif. op.) jobbról balra = += -= *= /= %= &= ^= = <<= >>= jobbról balra, (vessz op.) balról jobbra
M veletek precedenciája (prioritása, rend sége) több jelentés operátorok értelmezése a környezett l függ Példa: több jelentés operátorok címke: /* utasítás címke */ a=b?c:d; /* hozzárendelés, feltételes kif. */ int a=1; /* inicializáció */ a=(b+c)*d; /* zárójeles kif. */ fv(a, b); /* fv. hívás */ a, b; /* vessz s kif. */ fordító átrendezheti a kifejezést hatékonyság javítás miatt (ld. kommutatív, asszociatív m veletek); kiértékelés iránya nem hat az értékre, de problémás, ha ua. objektumot többször módosítjuk ua. objektum értékét változtatjuk és felhasználjuk Nem egyértelm eredmény i = t[i++]; i = a+++t[a]; a=0; b = (a=1)+(++a); /* ezen a zárójelezés sem segít! */
M veletek precedenciája (prioritása, rend sége) Néha segít a zárójelezés: k = (a+b)*2; Máskor nem: k = (a + b) + (c + d); A kiértékelési sorrend néha rögzített: l = a && b && c; Keletkezik mellékhatás? l = a && ++b; Függvény paramétereinek kiértékelési sorrendje sem rögzített: printf("%d %d", ++i, fv(i)); Ne keverjük a relációkat és a hozzárendelést! if(i=1) ut1 ; else ut2 ;