KURZUS: Programozás alapjai
MODUL: II. modul
5. lecke: Műveletek és kifejezések
Ebben a leckében elsősorban a korábban már megismert operátorok és a segítségükkel megalkotott kifejezésekre vonatkozó szabályokat pontosítjuk. További operátorokat ismerünk meg (pl. feltételes és bit szintűek), és megvizsgálunk olyan speciális helyzeteket is, amikor az operátorok valamilyen tulajdonságát kihasználva egyszerűsíthetünk a kódunkon. Külön felhívjuk a figyelmet a leggyakoribb programozói hibákra, veszélyekre is. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A lecke végére a hallgatónak tisztában kell lennie: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A lecke kulcsfogalmai: operátor, operandus, kifejezés, egész előléptetés, implicit, szabványos, explicit és hozzárendelési típuskonverzió, hordozható program, precedencia, rendűség, prioritás. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A műveletek és kifejezések használatának általános szabályai | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A műveleteket a nyelv operátorokkal (műveleti jelekkel) valósítja meg. A műveletek lehetnek: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
operátor: (a következők egyike!) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A kifejezés operátorok, operandusok (és elválasztó-jelek) sorozata, mely az alábbi tevékenységek valamilyen kombinációját valósítja meg: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A kifejezésbeli operandusokat elsődleges kifejezésnek nevezik. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
elsődleges-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A konstansokat, a karakterláncot tárgyaltuk az előző leckében, a hozzárendelés-kifejezést definiálni fogjuk a hozzárendelés operátoroknál. Az azonosító lehet bármilyen egész vagy lebegőpontos típusú. Lehet enum, tömb, mutató, struktúra, unió, vagy függvény típusú. Lehet tehát: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Összesítve: az azonosítónak balértéknek vagy függvényhívásnak kell lennie. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A kifejezés kiértékelése bizonyos | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
szabályokat követ, mely függ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A kifejezések különfélék lehetnek: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
utótag-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kifejezéslista: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
egyoperandusos-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
egyoperandusos-operátor: ( a következők egyike!) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
előtag-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
típusnév: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
absztrakt-deklarátor: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
direkt-absztrakt-deklarátor: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A típusnév az adattípus típusneve. Szintaktikailag az adott típusú objektum olyan deklarációja, melyből hiányzik az objektum neve. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A hozzárendelés-kifejezést, melyről most csak annyit jegyzünk meg, hogy nem balérték, majd a hozzárendelési műveleteknél ismertetjük! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Aritmetikai műveletek (+, -, *, / és %) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Közülük a legmagasabb prioritási szinten az egyoperandusos, jobbról balra kötő előjel operátorok vannak. Létezik a | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
- előtag-kifejezés | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
és a szimmetria kedvéért a | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+ előtag-kifejezés. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az előjel operátort követő előtag kifejezésnek aritmetikai típusúnak kell lennie, s az eredmény az operandus értéke (+), ill. annak -1-szerese (-). A + művelet egész operandusát egész-előléptetésnek (integral promotion) veti alá a fordító, s így az eredmény típusa az egész-előléptetés végrehajtása után képzett típus. A - műveletet megelőzheti implicit típuskonverzió, és egész operandus esetén az eredmény az operandus értékének kettes komplemense. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az egész-előléptetéssel az implicit típuskonverzió kapcsán rögtön foglalkozunk! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A többi aritmetikai operátor mind kétoperandusos, melyek közül a szorzás (*), az osztás (/) és a modulus (%) magasabb prioritási szinten van, mint az összeadás (+) és a kivonás (-). A szorzást, az osztást és a modulust multiplikatív operátoroknak, az összeadást és a kivonást additív operátoroknak is szokás nevezni. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Multiplikatív operátorok (*, / és %) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
multiplikatív-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Nézzük a műveletek pontos szabályait! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Készítsünk programot, ami beolvas egy négyjegyű évszámot, és eldönti róla, hogy szökőév-e, vagy sem! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A Gergely-naptár szerint szökőév minden, néggyel maradék nélkül osztható év. Nem szökőév a kerek évszázad, de a 400-zal maradék nélkül oszthatók mégis azok. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Írjunk intnume(char s[]) függvényt, mely 1-et (igazat) ad vissza, ha a paraméter karakterlánc tiszta numerikus, és zérust (hamisat), ha nem! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int nume(char s[]){ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Írjunk int atoi(char s[]) függvényt, mely megvalósítja ezt a konverziót, s n lesz a visszaadott értéke! Az átalakítást végezze az első nem konvertálható karakterig! Engedjük meg, hogy a numerikus karakterlánc elején fehér karakterek és előjel is lehessen! Ha az előjelet elhagyják, akkor legyen a szám pozitív! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int atoi(char s[]){ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Megemlítendő, hogy pontosan ilyen prototípusú, nevű és funkciójú függvény létezik a szabvány könyvtárban is, de bekapcsolandó hozzá a szabványos stdlib.h fejfájl. A könyvtárban van atol rutin, mely long-gá, és van atof, mely double-lé alakítja numerikus karakterlánc paraméterét. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Jöhet a program, de helyszűke miatt a függvények definícióit nem ismételjük meg! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/* PELDA14.C: A negyjegyu evszam szokoev-e? */ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Additív operátorok (+ és -) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
additív-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az additív operátorok csoportosítása is balról jobbra történik. Operandusaik az aritmetikai értékeken túl mutatók is lehetnek. (A mutatóaritmetikát majd a mutatók kapcsán ismertetjük!) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Aritmetikai operandusok esetén az eredmény a két operandus értékének összege (+), ill. különbsége (-). Egész vagy lebegőpontos operanduson a művelet implicit típuskonverziót is végezhet, ha szükséges. Ilyenkor az eredmény típusa a konvertált típus. Miután a konverziónak nincsenek túl vagy alulcsordulási feltételei, értékvesztés következhet be, ha az eredmény nem fér el a konverzió utáni típusban. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Matematikai függvények | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A matematikai függvények nem részei a C nyelvnek. Nyilvánvaló viszont, hogy kifejezések képzésekor szükség lehet rájuk. A C filozófiája szerint a matematikai függvények családját is a szabvány könyvtárban kell elhelyezni, mint ahogyan a szabvány bemenet és kimenet kezelését végző rutinokat. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az ANSI szabvány pontosan rögzíti ezeket a könyvtári funkciókat, így bármilyen szabványos C fordító és operációs rendszer számára kompatibilis formában létezniük kell. Magyarán: azok a programok, melyek az operációs rendszerrel való kapcsolatukat a szabvány könyvtáron át valósítják meg, minden változtatás nélkül átvihetők az egyik számítógépről a másikra, az egyik operációs rendszerből a másikba. Ezek az úgy nevezett portábilis programok. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A szabvány könyvtár függvényeit, típusait és makróit szabványos fejfájlokban deklarálták. Ezek közül néhánnyal már találkoztunk, másokkal meg még nem: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A matematikai függvények prototípusai a math.h fejfájlban helyezkednek el, így használatuk előtt ez a fejfájl bekapcsolandó! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#include <math.h> | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Nem kívánjuk felsorolni és részletezni az összes fejfájlt, az összes függvényt, csak néhány fontosabbat említünk meg közülük. Az olvasótól azonban elvárjuk, hogy a programfejlesztő rendszere segítségéből a további fejfájlokról és rutinokról is tájékozódjék. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A matematikai függvények double értéket szolgáltatnak, s néhány kivételtől eltekintve, paramétereik is double típusúak. A matematikából ismeretes korlátozások természetesen érvényben maradnak rájuk. A trigonometrikus függvények paramétere, ill. inverzeik visszaadott értéke radiánban értendő. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Néhányat felsorolunk a teljesség igénye nélkül! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A szabvány könyvtári függvények, így a matematikaiak is, a hibát úgy jelzik, hogy valamilyen speciális értéket (HUGE_VAL, zérus stb.) adnak vissza, és beállítják a UNIX-tól örökölt, globális | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
extern int errno; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(hibaszám) változót a hiba kódjára. A hibakódok az errno.h fejfájlban definiált, egész, nem zérusértékű szimbolikus állandók. A HUGE_VAL a legnagyobb, pozitív, még ábrázolható double érték. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A matematikai rutinok az értelmezési tartomány hibát EDOM értékű errno-val, és a fordítótól is függő függvény visszatérési értékkel jelzik. Értékkészlet probléma esetén az errno ERANGE. A függvény visszatérési érték túlcsorduláskor előjel helyes HUGE_VAL, ill. alulcsorduláskor zérus. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az értelmezési tartomány hiba akkor fordul elő, ha a függvény aktuális paraméterének értéke nincs benn az értelmezési tartományban. Értékkészlet hiba egyértelműen az, ha az eredmény nem ábrázolható double értékként. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Például az sqrt(-1.) hatására az errno EDOM, és a visszakapott érték negatív HUGE_VAL. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Reláció operátorok ( >, >=, <, <=, == és !=) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A reláció operátorok prioritása - eltekintve az egyoperandusos műveletektől - az aritmetikai és a logikai operátorok között helyezkedik el. A reláció operátorok két prioritási szintet képeznek, ahol az "igazi" relációk (>, >=, < és <=) prioritása magasabb az egyenlőségi relációkénál (== és !=). Az összes reláció az első operandus értékét hasonlítja a másodikéhoz, és a reláció érvényességét vizsgálja. Az eredmény logikai érték (int típusú), mely 1, ha a reláció igaz és 0, ha nem. A definíciók: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
relációs-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
egyenlőségi-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az eltolás-kifejezést a bitenkénti eltolás operátoroknál definiáljuk! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A relációk operandusai egész, lebegőpontos, vagy mutató típusúak. Az operandusok típusa különbözhet. Az operátorok implicit típuskonverziót is végrehajthatnak aritmetikai operandusaikon a művelet elvégzése előtt. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ne feledjük, hogy a | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kifejezés != 0 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
reláció mindig rövidíthető | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kifejezés | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
módon, mert a nyelvben a nem zérus érték logikai igaznak minősül. Példaként tekintsük meg újra a korábbi szakaszokban ismertetett atoi és getline függvényeket! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Logikai műveletek (!, && és ||) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Közülük a legmagasabb prioritási szinten az egyoperandusos, jobbról balra kötő, logikai nem operátor van, melynek alakja: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
! előtag-kifejezés | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ahol az előtag-kifejezés operandusnak egész, lebegőpontos, vagy mutató típusúnak kell lennie. Az eredmény mindenképpen int típusú, s az operandus logikai negációja. Az eredmény 0, ha az operandus értéke nem zérus, ill. 1, ha az operandus értéke zérus. Ez utóbbi mondatrész biztosítja, hogy a | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kifejezés == 0 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mindenkor rövidíthető | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
! kifejezés | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
módon. Például a multiplikatív operátoroknál ismertetett program részlet | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if( ev%4 == 0 && ev%100 != 0 || ev%400 == 0) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
utasítása így rövidíthető: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if( !(ev%4) && ev%100 || !(ev%400)) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Két kétoperandusos logikai művelet van a nyelvben a logikai és (&&) és a logikai vagy (||), melyek prioritása alacsonyabb a relációkénál és a bit szintű műveleteknél. A logikai és prioritása ráadásul magasabb, mint a logikai vagyé. Mindkét művelet balról jobbra csoportosít. Egyik operátor sem hajt végre implicit típuskonverziót operandusain, ehelyett zérushoz viszonyítva értékeli ki őket. Az eredmény int típusú (1 - igaz és 0 - hamis). | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logikai-és-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logikai-vagy-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A vagy-kifejezés definícióját a bit szintű műveleteknél találjuk meg! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A K1&&K2 kifejezés eredménye igaz (1), ha K1 és K2 egyike sem zérus. A K1||K2 kifejezés igaz (1), ha K1 és K2 valamelyike is nem zérus. Máskülönben K1&&K2 és K1||K2 eredménye hamis (0). | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Mindkét operátor esetében garantált a balról jobbra történő végrehajtás. Először K1-et értékeli ki a fordító az esetleges összes mellékhatásával együtt, de: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ha valami előbbre való, vagy mindenképp szeretnénk, hogy megtörténjen, akkor azt a bal oldali operandusba kell beépíteni. Például a pelda10.c-ben megírt getline for ciklusának feltétele nem véletlenül | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
i<n && (c=getchar())!=EOF && c!='\n' | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sorrendű, hisz először azt kell biztosítani, hogy a paraméter karaktertömböt ne írhassa túl a függvény. Ez nem kerülhet hátrébb a kifejezésben. Aztán a következő karaktert előbb be kell olvasni a bemenetről, de mindennek vége van fájlvég esetén. Itt sincs értelme a felcserélésnek, mert felesleges vizsgálgatni a fájlvéget, hogy soremelés-e. A relációk közti és műveletek miatt látszik, hogy balról jobbra történik az operandusok kiértékelése, és ha eközben az egyik hamis lesz, teljesen felesleges lenne továbbfolytatni a kiértékelést. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Implicit típuskonverzió és egész-előléptetés | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ha kétoperandusos (például aritmetikai) műveleteknél különbözik a két operandus típusa, akkor a művelet elvégzése előtt a fordító belső konverziót (átalakítást) hajt végre. Általában a pontosabb operandus típusára konvertálja a másikat. A kétoperandusos művelet eredményének típusa a konvertált típus lesz. Ezt a fajta konverziót szabványosnak, szokásosnak is nevezik. A szabályok nem prioritási sorrendben a következők: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ne feledjük azonban el, hogy a konverzió mindig függvényhívást jelent (gépidő!), azaz szükségtelenül ne alkalmazzuk! Csak "értelmes" típuskonverziókat valósít meg a fordító. Például az f + i összeadás végrehajtása előtt - feltéve, hogy f float és i int típusú - i értéke (és nem i maga!) float-tá alakul. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az "értelmetlen" lebegőpontos kifejezés indexben még csak megvalósul úgy, hogy a kifejezés értéke tört részét levágja a fordító | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#include <stdio.h> | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
azaz 3 lesz az eredmény. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Numerikus karakterlánc azonban sohasem alakul automatikusan aritmetikai értékké. Ehhez valamilyen konverziós függvényt kell használni. Az stdlib.h-beli atoi-ról, atol-ról és atof-ról volt már szó! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Típusmódosító szerkezet | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az implicit (szokásos, szabványos stb.) konverziókon túl magunk is kikényszeríthetünk (explicit) típuskonverziót a | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(típusnév) előtag-kifejezés | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
alakú, a Alapismeretek 1/2 leckében megismert típusmódosító szerkezet alkalmazásával. Látjuk, hogy a típusmódosító szerkezet egyoperandusos, s ez által magas prioritású művelet. A definícióban a típusnév a céltípus, és az előtag-kifejezés értékét erre a típusra kell konvertálni. Az előtag-kifejezést úgy konvertálja a fordító, mintha az értéket egy típusnév típusú változó venné fel. Az explicit típuskonverzió tehát a hozzárendelési konverzió szabályait követi. A legális típusmódosítások: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Példaként vegyük a matematikai függvények közül a négyzetgyököt, azaz: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#include <math.h> | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Programunkban van egy int típusú n változó, akkor az n+26 pozitív gyökét az | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sqrt(double(n+26)) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
függvényhívással kaphatjuk meg. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bármilyen azonosító, vagy kifejezés típusa módosítható void-dá. A típusmódosításnak alávetett azonosító, vagy kifejezés nem lehet azonban void. A void függvény hívását például hiába típusmódosítjuk int-re, a semmiből nem lehet egészet csinálni. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A void-dá módosított kifejezés értéke nem képezheti hozzárendelés tárgyát. Hasonlóan az explicit típusmódosítás eredménye nem fogadható el balértékként hozzárendelésben. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Kifejezést csak olyan helyen módosíthatunk void-dá, ahol az értékére nincs szükség. Például nincs szükség a bejövő gombnyomásra: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
printf("A folytatáshoz üssön Entert-t! "); (void)getchar(); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sizeof operátor | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az Alapismeretek 2/2 leckéből ismert sizeof egyoperandusos, jobbról balra kötő, magas prioritású művelet, mely mindig az operandusa tárolásához szükséges memória mennyiségét szolgáltatja bájtban. Az eredmény size_t típusú egész érték. Az stddef.h fejfájlban megtekintve a típust többnyire azt látjuk, hogy unsigned int. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Két különböző alakja van az operátornak: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sizeof(egyoperandusos-kifejezés) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sizeof(egyoperandusos-kifejezés) esetén 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. Például a tomb tömb elemszáma a következő konstrukcióval állapítható meg: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sizeof(tomb) / sizeof(tomb[0]) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A sizeof nem használható függvényre, vagy nem teljes típusú kifejezésre, ilyen típusok zárójelbe tett nevére, vagy olyan balértékre, mely bitmező objektumot jelöl ki. Bátran alkalmazható előfeldolgozó direktívákban is! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#define MERET sizeof(int)*3 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Inkrementálás (++), dekrementálás (--) és mellékhatás | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ezek az operátorok mind egyoperandusosak, s ezért magas prioritásúak. Mindkét operátor létezik utótag | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
utótag-kifejezés++ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
és előtag műveletként: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
++ egyoperandusos-kifejezés | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az inkrementáló és dekrementáló kifejezésben az utótag- vagy az egyoperandusos-kifejezésnek skalár (aritmetikai vagy mutató) típusúnak és módosítható balértéknek kell lenniük, de az eredmény nem balérték. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az inkrementálásnál (++) a balérték eggyel nagyobb, dekrementálásnál (--) viszont eggyel kisebb lesz. Előtag operátornál a "konstrukció" értéke egyezik az új balértékkel, míg utótag operátornál a "konstrukció" értéke az inkrementálás vagy dekrementálás végrehajtása előtti érték. Az eredmény típusát az operandus típusa határozza meg. Például: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int x, i = 3, j = 4, n = 5; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A kifejezés produkálhat | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A kifejezésnek ezen kívül lehet mellékhatása is. Például a Típusok és konstansok leckében megírt strcopy záró sorában | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
while(cél[i++]=forrás[i]) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
a hozzárendelés mellékhatásaként az i végrehajtás utáni értéke is eggyel nagyobb lesz. A mellékhatást a kifejezés kiértékelése okozza, s akkor következik be, ha megváltozik egy változó értéke. Minden hozzárendelés operátornak van mellékhatása. Láttuk, hogy a balértékre alkalmazott inkrementálási és dekrementálási műveletnek is van. Függvényhívásnak is lehet azonban mellékhatása, ha globális hatáskörű objektum értékét változtatja meg. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Írjuk meg a voidchdel(char s[], int c)-t, mely saját helyen törli a benne előforduló c karaktereket az s karakterláncból! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Itt is másolni kell a forrásból a célba bájtról-bájtra haladva, de a c értékű karaktereket ki kell ebből hagyni. Két indexre van szükség. Az egyik az i, mely végigjárja a forrást. A másik a j, mely a célban mindig a következő szabad helyet éri el. A nem c értékű karaktert a következő szabad helyre kell másolni, s a célbeli indexnek az ezután következő szabad helyre kell mutatnia. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
void chdel(char s[], int c){ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bit szintű operátorok ( ~, <<, >>, &, ^ és |) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A bit szintű operátorok csak signed és unsigned egész típusú adatokra: char, short, int és long használhatók. Legmagasabb prioritási szinten az egyoperandusos, jobbról balra kötő egyes komplemens operátor (~) van, melynek definíciója: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
~ előtag-kifejezés | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az operátor előbb végrehajtja az egész-előléptetést, ha szükséges. Az eredmény típusa az operandus konverzió utáni típusa. Az eredmény maga a bit szintű egyes komplemens, azaz ahol az operandus bit 1 volt, ott az eredmény bit 0 lesz, és ahol az operandus bit 0 volt, ott az eredmény bit 1 lesz. Feltéve, hogy az egész-előléptetés 16 bites, és hogy: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
unsigned short x = 0XF872, /* 1111100001110010 */ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
akkor a ~x 00000111100011012, és a ~maszk 00001111000011112. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A balról jobbra csoportosító eltolás operátorok (<< és >>) prioritása alacsonyabb az aritmetikai műveletekénél, de magasabb, mint a reláció operátoroké. Az eltolás operátorok első operandusuk értékét balra (<<) vagy jobbra (>>) tolják annyi bitpozícióval, mint amennyit a második operandus meghatároz. A definíció a következő: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
eltolás-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A K1<<K2 és a K1>>K2 kifejezések esetében minkét operandus egész típusú kell, legyen. Az operátorok egész-előléptetést is megvalósíthatnak. Az eredmény típusát K1 konvertált típusa határozza meg. Ha K2 negatív vagy értéke nem kisebb K1 bitszélességénél, akkor az eltolási művelet eredménye határozatlan (azaz fordítóprogramonként eltérő lehet). | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Miután a C-ben nincs egész alul vagy túlcsordulás, a műveletek értékvesztést is okozhatnak, ha az eltolt eredmény nem fér el az első operandus konvertált típusában. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A K1<<K2 balra tolja K1 értékét K2 bitpozícióval úgy, hogy jobbról 0 bitek jönnek be. K1 túlcsordulás nélküli balra tolása ekvivalens K1*2K2-vel. Ilyen értelemben aztán az eltolás aritmetikai műveletnek is tekinthető. Ez a gondolatsor igaz persze az összes többi bit szintű műveletre is! Ha K1 valamilyen signed típus, akkor az eltolás eredménye csak "gonddal" szemlélhető az előjel bit esetleges kitolása miatt. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A K1>>K2 művelet K1 értékét K2 bitpozícióval tolja jobbra. Ha K1 valamilyen unsigned típusú, akkor balról 0 bitek jönnek be. Ha K1 signed, akkor az operátor az előjel bitet sokszorozza. unsigned, nem negatív K1 esetén a jobbra tolás K1/2K2 hányados egész részeként is interpretálható. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Folytatva az egyes komplemensképzésnél megkezdett példát, az x<<2 11100001110010002, ill. a maszk>>5 00000111100001112. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A bit szintű logikai operátorok prioritásuk csökkenő sorrendjében az és (&), a kizáró vagy (^), valamint a vagy (|). A többi műveletre való tekintettel prioritásuk magasabb a kétoperandusos logikai operátorokénál, de alacsonyabb a relációkénál. Lássuk a definíciót! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
és-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kizáró-vagy-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
vagy-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(Az egyenlőségi-kifejezés definíciója a relációknál megtalálható.) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ha szükséges, akkor a művelet elvégzése előtt a fordító implicit típuskonverziót hajt végre az egész típusú operandusok értékén. Az eredmény típusa az operandusok konverzió utáni típusa. A művelet bitről-bitre valósul meg az operandusok értékén, s egy bitre vonatkoztatva az eredmény így néz ki: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Befejezve az egyes komplemensnél megkezdett példát, az x|maszk értéke 11111000111100102. Állíthatjuk, hogy az eredményben minden olyan bit egy, ami a maszk-ban az volt. Az x^x eredménye biztosan tiszta zérus. Az x&~maszk értéke 00001000000000102. Megemlítjük, hogy az eredmény minden olyan bitpozícióján 0 van, ahol a maszk bitje 1. Tehát a kifejezés azokat a biteket bizonyosan törölte, ahol a maszk bit 1 volt. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Nem szabad összekeverni a logikai vagy (||) és a bitenként vagy (|), ill. a logikai és (&&) és a bit szintű és (&) műveleteket! Feltéve, hogy két, egész típusú változó értéke: a=2 és b=4, akkor az | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
a && b -> 1 (igaz) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
de az | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
a & b -> 0 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az ANSI szabvány szerint a bit szintű műveletek signed egészeken implementációfüggők. A legtöbb C fordító azonban signed egészeken ugyanúgy dolgozik, mint unsigned-eken. Például short int-ben gondolkodva a -16 & 99 eredménye 96, mert: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1111111111110000&0000000001100011 -> 0000000001100000 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A fájl utolsó módosításának dátumát és idejét egy-egy szóban, azaz C nyelvi fogalmakkal: egy-egy unsigned short int-ben, 16 biten tároljuk. A két szó bitfelosztása legyen a következő: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A két szó azonosítója legyen datum és ido! Tételezzük fel, hogy az idő részeit az ora, a perc és az mpunsigned short változókban tároljuk! Az ugyanilyen unsigned short változók a dátum részeire: ev, ho és nap. Akkor az oda-visszaalakítás a következő: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/* Részeiből az idő szó előállítása: */ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Írjunk unsigned long invertal(unsigned longx, int p, int n) függvényt egy rövid kipróbáló programmal, mely az x paramétere értékét a p.-ik bitpozíciójától n hosszban invertálja (az 1-eseket 0-kra, s a 0-kat 1-esekre cseréli)! A nem említett bitpozíciók értéke maradjon változatlan! Az invertált eredmény a függvény visszatérési értéke. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az ábra x paramétert, a zérustól induló, 20 értékű p bitpozíciót és az n=5 bitszélességet szemlélteti. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Készítsünk előbb egy ugyancsak unsigned long maszkot, mely p pozíciótól n szélességben 1-es biteket tartalmaz, és az ezen kívüli pozíciók mind nullák! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
~0: 1111111111111111111111111111111 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ezután x egyes komplemenséből bitenkénti éssel kivágjuk a kérdéses invertált bitpozíciókat, azaz: ~x&maszk. Ezt aztán bitenkénti vagy kapcsolatba hozzuk az eredeti x egy olyan változatával, melyben kinulláztuk az érdekes biteket, azaz: x&~maszk. Tehát: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/* PELDA15.C: Bitpoziciok invertalasa. */ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Vegyük észre, hogy a binaris függvény segítségével unsigned long értéket jelentetünk meg binárisan! A maszk változó 31-es bitpozíciójától indítunk egy 1-est, s a ciklusmag végrehajtása után mindig eggyel jobbra toljuk. A maszk és az érték bit szintű és kapcsolata akkor szolgáltat nem zérust (igazat), ha a kérdéses bitpozíción az értékben 1 van. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Feltételes kifejezés (? :) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ez a nyelvben az egyetlen három operandusos művelet, melynek prioritása alacsonyabb a kétoperandusos logikai operátorokénál. A definíció: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
feltételes-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A logikai-vagy-kifejezésnek egész, lebegőpontos vagy mutató típusúnak kell lennie, s kiértékelése zérushoz való hasonlítását jelenti: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Tehát a logikai-vagy-kifejezést mindenképpen kiértékeli a kód, de a kifejezés és a feltételes-kifejezés közül csak az egyik kiszámítása történik meg: a K1 ? K2 : K3 feltételes kifejezésben K1 értékétől függően K2-t vagy K3-at értékeli ki a fordító. A konstrukció eredményének típusa ilyen alapon K2 vagy K3 operandusok típusától függ a következőképp: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Például az | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if( a > b ) z = a; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
utasítás helyettesíthető a | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
z = a > b ? a : b; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
feltételes kifejezéssel. Ha például egy int típusú a tömb első N elemét szeretnénk megjelentetni úgy, hogy egy sorba egymástól szóközzel elválasztva 10 elem kerüljön, akkor ezt "tömör" kódot alkalmazva így is megtehetjük: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for(i=0; i<N; ++i) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Javítsunk ki néhány eddig megírt függvényt a feltételes kifejezés felhasználásával! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Mindkét példában a feltételes kifejezés logikai-vagy-kifejezése köré zárójelet tettünk. Lássuk azonban be, hogy erre semmi szükség sincs, hisz ennél alacsonyabb prioritású művelet már csak kettő van: a hozzárendelés és a vessző operátor! A felesleges zárójel legfeljebb a jobban olvashatóságot biztosítja. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Hozzárendelés operátorok | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A jobbról balra csoportosító hozzárendelés prioritása alacsonyabb, mint a feltételes kifejezésé, s ennél alacsonyabb prioritású művelet már csak a vessző operátor. A művelet a jobb oldali operandus értékét rendeli a bal oldali operandushoz, melyből következőleg a bal oldali operandusnak módosítható balértéknek kell lennie. A hozzárendelés kifejezés értéke ugyan egyezik a bal oldali operandus hozzárendelés végrehajtása utáni értékével, de nem balérték. A jobb oldali operandus értékét a fordító a bal oldali operandus típusára konvertálja a bal operandusba való letárolás előtt a hozzárendelési konverzió szabályai szerint. A bal oldali operandus nem lehet tömb, függvény vagy konstans. Nem lehet természetesen nem teljes (még nem teljesen deklarált) típusú sem. A definíció: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hozzárendelés-kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hozzárendelés-operátor: ( a következők egyike!) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Van tehát egyszerű hozzárendelés operátor (=) és vannak összetettek vagy kombináltak (ezek a többiek). | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Foglalkozzunk előbb az egyszerű hozzárendeléssel! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A K1 = K2 kifejezésben K1-nek módosítható balértéknek kell lennie. K2 értéke - esetlegesen a K1 típusára történt konverzió után - felülírja a K1 által kijelölt objektum értékét. Az egész "konstrukció" értéke K2 értéke az esetleg a K1 típusára szükségessé vált hozzárendelési konverzió végrehajtása után. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Reméljük, hogy nem felejtették még el, hogy a balérték (K1) és jobbérték (K2) fogalom éppen az egyszerű hozzárendelésből származik. A balérték a hozzárendelés operátor bal oldalán, a jobbérték pedig a jobb oldalán állhat. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Tudjuk, ha egy kifejezésben hozzárendelés operátor is van, akkor annak a kifejezésnek bizonyosan van mellékhatása. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Emlékezzünk vissza, hogy a definíció megengedi a hozzárendelés operátor | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
K1 = K2 = K3 = ... = Kn = kifejezés | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
formájú használatát is, amikor is a kifejezés kiértékelése után jobbról balra haladva az operandusok felveszik a kifejezés értékét. Az egész konstrukció értéke most is a kifejezés értéke lesz. Például | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
a = b = c = d + 6; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A kombinált hozzárendelés operátorok a | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
K1 = K1 operátor K2 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kifejezést | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
K1 operátor = K2 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
módon rövidítik, és K1 kiértékelése csak egyszer történik meg. A megengedett operátorok a definícióban láthatók! Mindegyik megvalósítja azt a műveletet, konverziót és korlátozást, amit a kétoperandusos operátor egyébként realizál, és végrehajtja a hozzárendelést is. A kombinált hozzárendelés operátor operandusai egész vagy lebegőpontos típusúak lehetnek általában. A += és -= bal oldali operandusa mutató is lehet, amikor is a jobb oldali operandus köteles egész típusú lenni. Például: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
x = x * ( y + 6); -> x *= y + 6; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az összetett operátorokat használva kevesebbet kell írni. Például: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
t[ t1[i3 + i4] + t2[i1 - i2] ] += 56; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Használjunk kombinált hozzárendelés operátorokat néhány eddig már megírt függvényben! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Hozzárendelési konverzió | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Hozzárendelésnél a hozzárendelendő érték típusát a hozzárendelést fogadó változó típusára konvertálja a fordító. A C megengedi a hozzárendelési konverziót lebegőpontos és egész típusok között azzal, hogy a konverziónál értékvesztés történhet. A használatos konverziós módszer követi az implicit típuskonverzió szabályait és ezen túl még a következőket: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Konverzió más típusokról: Nincs konverzió a struktúra és az unió típusok között. Explicit típusmódosítással bármilyen érték konvertálható void típusúvá, de csak abban az értelemben, hogy a kifejezés értékét elvetjük. A void típusnak definíció szerint nincs értéke. Ebből következőleg nem konvertálható más típusúra, s más típus sem konvertálható void-ra hozzárendeléssel. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Vessző operátor | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ez a legalacsonyabb prioritású művelet. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kifejezés: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A K1, K2, ..., Kn esetén balról jobbra haladva kiértékeli a fordító a kifejezéseket úgy, hogy a bennük foglalt minden mellékhatás is megvalósul. Az első n - 1 kifejezés void-nak tekinthető, mert az "egész konstrukció" típusát és értékét a legjobboldalibb kifejezés típusa és értéke határozza meg. Például a | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
regikar = kar, kar = getchar() | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
esetén regikar felveszi kar pillanatnyi értékét, aztán kar és az egész kifejezés értéke a szabvány bemenetről beolvasott karakter lesz. Tipikus példa még a több kezdőérték adás és léptetés a for utasításban. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Írjuk meg egy rövid kipróbáló programmal a void strrv(char s[]) függvényt, mely saját helyén megfordítja a paraméter karakterláncot! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Az algoritmusról annyit, hogy a karakterlánc első karakterét meg kell cserélni az utolsóval, a másodikat az utolsó előttivel, és így tovább. Két indexet kell indítani a karaktertömbben: egyet alulról és egyet felülről. Az alsót minden csere után meg kell növelni eggyel, a felsőt pedig ugyanennyivel kell csökkenteni. A ciklus tehát addig mehet, míg az alsó index kisebb a felsőnél. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/* PELDA16.C: Karakterlanc megforditasa. */ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A string.h fejfájlt bekapcsolva rendelkezésre áll az strrv szabvány könyvtári változata, mely ugyanilyen paraméterezésű és funkciójú, de strrev a neve, s más egy kicsit a visszatérési értékének a típusa. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Vegyük észre, hogy a main-beli while-ban is vesszős kifejezést használtunk! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Olyan szövegkörnyezetben, ahol a vessző szintaktikai jelentésű, a vessző operátort csak zárójelbe tett csoporton belül szabad használni. Ilyen helyek: az inicializátorlista például, vagy a függvény paraméterlistája. A | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fv(b, (a = 2, t += 3), 4); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kitűnően mutatja, hogy az fv függvény három paraméterrel rendelkezik. A hívásban a második paraméter vesszős kifejezés, ahol a előbb 2 értékű lesz, aztán t megnő hárommal, s ezt az értéket kapja meg a függvény is második aktuális paraméterként. Ha a függvény prototípusa mást nem mond, akkor a második paraméter típusa t típusa. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Műveletek prioritása | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A műveletek prioritását nevezik precedenciának, vagy rendűségnek is. Mindhárom esetben arról van szó, hogy zárójel nélküli helyzetben melyik műveletet kell végrehajtani előbb a kifejezés kiértékelése során. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A következő táblázat csökkenő prioritással haladva mutatja az egyes operátorok asszociativitását. A többször is előforduló operátorok közül mindig az egyoperandusos a magasabb prioritású. Abban a rovatban, ahol több operátor van együtt, a műveletek azonos prioritásúak, és asszociativitásuknak megfelelően hajtja őket végre a fordító. Minden operátor kategóriának megvan a maga asszociativitása (balról jobbra vagy jobbról balra köt), mely meghatározza zárójel nélküli helyzetben a kifejezés csoportosítását azonos prioritású műveletek esetén. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A táblázatban felsoroltakon kívül van még a # és a ## operátor, melyek az előfeldolgozónak szólnak. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A prioritási táblázat természetesen tartalmaz eddig még nem ismertetett műveleteket is. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Vannak többjelentésű operátorok is, melyek értelmezése a helyzettől függ. Például: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cimke: /* utasítás címke */ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A kifejezés kiértékelési sorrendje nem meghatározott ott, ahol a nyelvi szintaktika erről nem gondoskodik. A fordító a generált kód hatékonyságának javítása érdekében átrendezheti a kifejezést különösen az asszociatív és kommutatív operátorok (*, +, &, ^ és |) esetén, hisz azt feltételezi, hogy a kiértékelés iránya nem hat a kifejezés értékére. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Probléma lehet az olyan kifejezéssel, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Például: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
i = v[i++]; /* Döntsük el, mit is akarunk i-vel! */ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A fentiek akkor is igazak, ha kifejezésünket " jól összezárójelezzük": | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int osszeg=0; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Segédváltozók bevezetésével a dolgok midig egyértelműsíthetők: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int seged, osszeg=0; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ha a szintaktika rögzíti a kiértékelési sorrendet (az &&, a ||, a ?: és a vessző operátor esetében ez így van), akkor ott "bármit" megtehetünk. Például: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sum = (i=3, i++, i++); /* OK, sum == 4 és i == 5. */ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A kifejezés kiértékelési sorrendjét ugyan () párokkal befolyásolhatjuk: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
f = a*(b+c); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
de asszociatív és kommutatív operátorok operandusait még "agyonzárójelezve" is összecserélheti a fordító, hisz feltételezheti, hogy a kifejezés értékét ez nem befolyásolja: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
f = (a+b) + (c+d); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Határozatlan a függvény aktuális paramétereinek kiértékelési sorrendje is: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
printf("%d %d\n", ++n, fv(n)); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A bitenkénti operátorok prioritása alacsonyabb a relációkénál, így a | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
c & 0XF == 8 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
mindig hamis, mert az előbb kiértékelésre kerülő egyenlőségi reláció sohasem lehet igaz. A kifejezés helyesen: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(c & 0XF) == 8. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Vigyázzunk a hozzárendelés (=) és az egyenlő reláció (==) operátor felcserélésére, mert például az | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if( i = 2 ) utasítás1; else utasítás2; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
else ágára sohasem jut el a vezérlés tekintettel arra, hogy a 2 hozzárendelése "ebben az életben" sem válik hamissá. Ügyeljünk azokkal a kifejezésekkel is, melyekben csak logikai és (&&) vagy csak logikai vagy (||) műveletek vannak, mert ezeket balról jobbra haladva | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fogja kiértékelni a fordító! Az | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
x && y++ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kifejezésben y növelése csak akkor történik meg, ha x nem zérus. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Kifejezés kiértékelése közben adódhatnak "áthidalhatatlan" szituációk. Ilyenek | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Újra felhívjuk azonban a figyelmet arra, hogy a nyelvben nem létezik sem egész túl, sem alulcsordulás! |
Feladatok | |||||||||||||||||
1. Olyan int egesze(char s[]) függvényt szerettünk volna írni, ami a decimális egész konstans írásszabályát ellenőrzi a paraméter karaktertömbön. Sajnos nem sikerült. | |||||||||||||||||
/*01*/#define HSZ sizeof(int)/sizeof(short)*5 /* Az int max. jegyeinek száma. */ | |||||||||||||||||
Jelölje meg azokat a sorokat, amikkel a függvény helyes működésre bírható!
![]() | |||||||||||||||||
2. Olyan int hexae(char s[]) függvényt szerettünk volna írni, ami megvizsgálja, hogy paramétere hexadecimális szám-e. Sajnos nem sikerült. | |||||||||||||||||
/*01*/int hexae(char s[]) { /* A param. hexadec. karakterlanc-e? */ | |||||||||||||||||
Jelölje meg azokat a sorokat, amikkel a függvény helyes működésre bírható!
![]() | |||||||||||||||||
3. Olyan long atoh(char s[]) függvényt szerettünk volna írni, ami hexadecimális karakterláncot alakít egésszé. Sajnos nem sikerült. | |||||||||||||||||
/*01*/long atoh(char s[]) { /* Hexa. karakterlanc konverzioja. */ | |||||||||||||||||
Jelölje meg azokat a sorokat, amikkel a függvény helyes működésre bírható!
![]() | |||||||||||||||||
4. Olyan int ell210e(char s[]) függvényt szerettünk volna írni, ami teszteli, hogy s 2 és 10 közötti alapú, nem negatív szám-e. A számrendszer alapját fordítási időben változtatni (#define) lehet. Sajnos célunkat nem sikerült elérni. | |||||||||||||||||
/*1*/#define ALAP 10 | |||||||||||||||||
Jelölje meg azokat a sorokat, amikkel a függvény helyes működésre bírható!
![]() | |||||||||||||||||
5. Olyan int ella36e(char s[], int alap) függvényt szerettünk volna írni, ami teszteli, hogy salap alapú, nem negatív szám-e. A számrendszer alapja, vagyis alap értéke 2 és 36 közötti lehet. Sajnos célunkat nem sikerült elérni. | |||||||||||||||||
/*1*/int ella36e(char s[], int alap){ | |||||||||||||||||
Jelölje meg azokat a sorokat, amikkel a függvény helyes működésre bírható!
![]() | |||||||||||||||||
6. Készítsen long ato36(char s[], int alap) függvényt az atoi mintájára, amely a legfeljebb 36 alapú számrendszerbeli karakterláncot konvertálja egésszé! | |||||||||||||||||
long ato36(char s[], int alap){ | |||||||||||||||||
7. Olyan kifejezést szeretnénk írni, ami szolgáltatja az x n db. egymást követő bitje által meghatározott értéket a p pozíciótól kezdve. x, n és p típusa unsigned int. Jelölje meg a helyes megoldást!
![]() | |||||||||||||||||
8. Készítse el az unsigned long rotl(unsigned long x) függvényt, ami 1 bittel balra forgatja paramétere értékét. Tehát a 31. pozícióról kicsorgó bit lesz az eredmény 0. bitje. | |||||||||||||||||
#define HSZ sizeof(unsigned long)*8 /* A tipus bitszama. */ | |||||||||||||||||
9. Olyan unsigned rotr(unsigned szam) függvényt szerettünk volna írni, ami paraméterének értékét 1 bittel jobbra forgatva adja vissza (tehát ami a 0. bitnél kicsordul, azt áthelyezi a legmagasabb helyi értékű bitbe). Sajnos célunkat nem sikerült elérni. | |||||||||||||||||
/*1*/#define SZH sizeof(unsigned)*8 | |||||||||||||||||
Jelölje meg azokat a sorokat, amikkel a függvény helyes működésre bírható!
![]() |