C Segítség
Változók, pointerek
A gép memóriáját úgy célszerű elképzelni, mintha lennének 0-tól -ig (ez a memória méretétől függ) számozott dobozaim, mindegyikben 8 bit. Mivel alapvetően bitsorozatokat, és nem számokat, betűket, vagy bármi mást tudok tárolni, ezért ha ez utóbbiakra van szükségem, kell egy megfeleltetés, hogy melyik bitsorozat mit jelent (lásd pl. Digit1-ből a számábrázolásokat). C-ben azért kell a változóknak típust is adni, hogy a fordító tudja, milyen gépi utasításokat generáljon, ha pl. egy összeadást lát a forrásban. Ugyanis egészen más formátuma van a lebegőpontos számoknak (pl. float) és az egészeknek (pl. int), ezért bitszinten máshogy kell az összeadást is elvégezni.
Pár C-beli kifejezés, és hogy a fordító mit csinál a hatására:
- int num; (egy függvény törzsében)
- A fordító olyan kódot generál, ami a függvénybe belépéskor sizeof(int) darab egymás melletti bájtot lefoglal, és feljegyzi, hogy a num szimbólum egy int típusú dolgot jelent, és hogy melyik memóriaterületen lesz tárolva az, amit a "num"-ban kellene.
- num+=2;
- A fordító kikeresi a táblázatából, hogy num tartalma hol van tárolva, és milyen típusú. Ez alapján olyan kódot generál, ami a megfelelő memóriaterület tartalmát egy ideiglenes tárolóba másolja, egy megfelelő utasítással előállítja azt a bitmintát, ami a régi bitminta által jelentett számnál kettővel nagyobb számot jelenti, majd visszamásolja a num területére, felülírva a régi értéket.
- f=num+0.5;
- A fordító észreveszi, hogy különböző típusú dolgok összeadása szerepel a kódban. A C nyelv ún. implicit típuskonverziós szabályai azt írják elő, hogy egy egész és egy lebegőpontos szám összeadását úgy kell elvégezni, hogy az egészet is lebegőpontossá kell alakítani, majd így hozzáadni a másik lebegőpontosat. Ezért olyan kódot generál, ami egy ideiglenes memóriaterületre előállítja azt a bitmintát, ami a float típus szerint azt a számot jelenti, amit a num bitmintája az int típus szabálya szerint jelentene. Majd, további utasítások az ideiglenes memóriaterületen elvégzik a float bitreprezentációja szerint az összeadást, és az eredményt átmásolja az f által jelentett memóriaterületre. (Hacsak f típusa nem tesz szükségessé újabb konverziót...)
- printf("%d", num+4);
- Ez egy összetett kifejezés, aminek külső "rétege" egy függvényhívás (a zárójel itt a függvényhívás operátor), a belső pedig egy összeadás. A fordító "belülről kifelé" generál ebből kódot: először egy ideiglenes helyre kiszámítja num+4 értékét, majd ezt bemásolja egy speciális memóriaterületre (stack/verem), ahol a printf majd keresni fogja. A printf még egy dolgot kap: az első paramétere egy karaktersorozat (
"%d"
). Ezt a karaktersorozatot a fordító eltárolja a generált .exe fájlban az adatoknak fenntartott helyen, majd olyan kódot generál, ami ennek a címét (pontosabban azt a címet, ahova a .exe memóriába töltésekor másolódik a string) is átmásolja a stackre. Ez után megjegyzi, hogy itt majd a "printf" nevű dolgot kell hívni (és majd a linker a megfelelő könyvtárból kimásolja a kódját, és behelyettesíti ide a címet, ahova a kód kerül).
- Ez egy összetett kifejezés, aminek külső "rétege" egy függvényhívás (a zárójel itt a függvényhívás operátor), a belső pedig egy összeadás. A fordító "belülről kifelé" generál ebből kódot: először egy ideiglenes helyre kiszámítja num+4 értékét, majd ezt bemásolja egy speciális memóriaterületre (stack/verem), ahol a printf majd keresni fogja. A printf még egy dolgot kap: az első paramétere egy karaktersorozat (
- f=sqrt(2);
- Itt is legenerálódik a függvényhívást végző kód. A különbség: miután a függvény végzett, még a visszatérő utasítás előtt egy adott helyre másolja az értéket, amit vissza akar adni (a 2 kiszámított gyökét). A függvényhívás után az érték erről a helyről átmásolódik az f által jelölt helyre.
- int *p;
- A csillag operátor több dolgot is jelent C-ben; ha csak egy operandusa van, akkor jelentése: "a ... által mutatott dolog" (pointer dereferálása, azaz "a referenciából nem-referenciát/normális értéket csinálás"). Amikor azt írom: "int m;" ez azt jelenti: "az m szimbólum egy int". A fenti deklaráció így: "a p által mutatott dolog egy int" (lásd lent, hogy mit jelent a "rámutatás"). Vagyis, p-t arra fogom használni, hogy egy memóriacímet tároljak benne, méghozzá egy olyat, ahol egy int-et fogok tárolni.
- a=*p;
- A generált kód a következőt fogja végezni: kiolvas a memóriából a p-nek fenntartott helyről valahány bájtot (ez a géptől függ, hogy hány bájt hosszú egy memóriacím) és megkeresi a memóriában azt a rekeszt, aminek pont az a bitsorozat a címe, amit kiolvasott; a továbbiakban ezzel és az utána következő néhány rekesszel dolgozik. A fordító ehhez felhasználja, hogy p egy int típusú dologra mutat, tehát az adott címen elvileg egy int tárolódik, így összesen sizeof(int) darab rekeszt kell majd használni. Majd az adott helyről az adott számú bájtot átmásolja (értékadás operátor) az "a" területére.
-- G - 2009.01.30.