C/C++ Kérdés a void pointerekről?
1. sizeof(void) -ra fordítási hibát ad. Miért?
2.
int x;
void * ptr = &x;
Itt a ptr nem jelöl ki memóriaterületet (könyv szerint). Ez mit jelent pontosan? Ha nem void pointert használok akkor a memóriaterületen létrejön egy pointer ami vhova mutat. A ptr számára nem jön létre külön pointerváltozó? De akkor ez azt jelentené hogy fordítási időben "létezik"?
3. Változót void típussal van értelme használni, vagy csak pointerek esetében?
void var;
Megköszönném ha segítenétek. Üdv
2. A ptr pointer változónak lefoglalódik egy új memóriaterület, és az x változó címe kerül bele.
-- "A ptr számára nem jön létre külön pointerváltozó?"
A ptr maga a pointer változó. A ptr memóriacímére itt most nem mutat semmi, pontosabban a "ptr" szimbólum hivatkozik rá, és ahol felhasználod, oda belefordítódik az a cím. De ez a cím nem változó, hanem egy konstans érték.
És mi a helyzet a sizeof(void*) -al? Ez lefordul, és 4 -et ad vissza.
De a fő kérdésem a 2-es. A void* mutatók ha nem jelölnek ki tárolót, akkor csak fordítási időben fejtik ki hatásukat?
3. Változót void típussal van értelme használni, vagy csak pointerek esetében? (tudom-tudom függvények, de ezen túlmenően akkor void típusú változókat nemigen használunk?)
"De ez a cím nem változó, hanem egy konstans érték."
A cím típusú adat nem egy pointer? Én úgy tudom.
3. A void nem típus. Pointerek esetében azt jelenti, hogy a pointer által mutatott byte-on nincs meghatározott típusú adat, vagyis tíőus nélküli pointer. De amúgy pointer, tehát a pointer típusú, csak a mutatott adat nincs deklarálva.
void var;
A fordító ilyenkor létrehoz magának egy "var" szimbólumot, és a típusának megfelelő méretű memóriát "foglal le". (Azért idézőjelben, mert majd csak a linker fogja a szimbólumokat konkrét címekre cserélni.) Viszont a lefoglalt méretre már a fordítónak is szüksége van, enélkül nem tud relatív hivatkozásokat sem létrehozni. Mivel a void nem típus, ezért mérete sincs.
A pointer azért lehet void*, mert az "csak" egy olyan változó, ami egy címet tárol. (Ezáltal mutat arra a címre.) Címet tárolni meg anélkül is lehet, hogy tudnánk mi van azon a címen.
Egyébként az 1. pontban nem sizeof(void*)-ot akartál írni?
Na, addig gépeltem, hogy nem is láttam, amit írtál. :)
"A void* mutatók ha nem jelölnek ki tárolót, akkor csak fordítási időben fejtik ki hatásukat?"
A mutató (pointer) olyan változó, ami egy címet tárol. Ez leggyakrabban 4 bájtos (32 bites) cím, ezért kapsz 4-et a sizeof(void*)-ra. Azt a méretet adja vissza, ahány bájton a címet tárolja.
Nem tudom, mit értesz azon, hogy "tárolót jelöl ki". Talán arra gondolsz, hogy mutat valahova, de ez nem jelent feltétlenül memóriafoglalást, csaj egy cím tárolását.
A pointer pedig egy típus. Nincs külön típusneve, tehát nem tudsz olyat mondani, hogy 'pointer p;', ehelyett azt a típust adjuk meg, ami típusú adat a mutatott helyen van. Ha nem akarjuk ezt magadni, akkor lehet void*-ot írni. A void szimbólum csak annyit jelent, hogy nem adunk meg típust, tehát a void* egy olyan pointer, ami pl. egy ismeretlen adatra mutat.
(Remélem a kérdésre válaszoltam. :D)
"A cím típusú adat nem egy pointer? Én úgy tudom."
Azt nem arra írtam, félreértettél. A pointer egy cím típusú adat, igen. A pointer egy címet tárol. (Én a ptr szimbólumról írtam, de csak mellékesen.:))
"A void* mutatók ha nem jelölnek ki tárolót, akkor csak fordítási időben fejtik ki hatásukat?"
Na jó, kezdem érteni, mire gondolsz. :)
Ha azt mondom, hogy
<típus/void> * változónév;
akkor létrejön egy pointer. Lefoglalódik neki (a te gépeden legalábbis) 4 bájt. Akkor is, ha int*, char*, vagy void*.
Az, hogy mi a típus a '*' előtt, az szól a fordítónak. Amúgy futásidőben mindegyik ugyanolyan cím lesz. A típus fogja viszont megmondani, hogy fordításkor hogyan kezelje pl. a *ptr hivatkozásokat. Ha int* típusú volt, akkor a mutatott helyről int-et olvas ki. Vagy ha egy int tömb első elemére mutat, és te a második elemet akarod kiolvani, akkor tudnia kell a fordítónak, hogy a mutatóhoz sizeof(int)-et kell hozzáadni, hogy a második elemre mutasson. Ezek fordításkor dőlnek el, és fixen kerülnek a kódba. Futásidőben már csak sima pointerek vannak, minden más be van "égetve" a kódba. :)
De a lényeg, hogy a void* is ugyanúgy memóriát foglal, tehát futáskor lefoglalódik neki is a 4 bájt.
Igen, a félreértést a "tárolót jelöl ki" kifejezés semmitmondósága okozza, de akkor ha jól értem, helyesek a következő összegzések?
1. A mutató tehát egy változó, aminek értéke egy memóriacím. A memóriacím alacsony szinten "számérték" (4 bájtos, típustól függetlenül) míg C/C++ kódokban egy pointer típus reprezentálja. (tudom az hogy hány bájtos a memóriacím implementációfüggő, jelen esetben win32).
2. A nem egyértelmű kifejezés miatt nekem először úgy jött le hogy a void* típusú változó nem jön létre a memóriaterületen, csak fordítási időben van jelentősége, de ezzel szemben akkor ugyanúgy 4 bájt hosszon létezik a létrejövő program kódjában. Típus nélkülisége miatt pedig a rá történő hivatkozáskor "tetszőleges" hosszúságú memóriaterület feldolgozható, ez a típuskonverziótól függ, ugye? Ezzel szemben az olyan mutatók amelyeket alaptípussal definiálunk mindig meghatározott hosszúságú memóriaterületet dolgoznak fel. Jól értem?
3.
void var;
A "var" szimbólum létezik a C/C++ programban (ezért tehát pl nem lehet újabb "var" azonosítót definiálni) viszont mivel nincs típusa végeredményben nem jön létre változó?
Végezetül egy utolsó kérdésem van ami nem a void-al kapcsolatos de idevág:
A "címe" & művelet az adott objektum memóriacímét adja vissza. C/C++ban a memóriacímeket pointerek reprezentálják, ez azt jelenti hogy visszaad egy pointert, aminek értéke az objektum memóriacíme (úgyis mondhatjuk hogy az objektum címére mutat).
pointer = &variable;
A konkrét kérdésem, hogy az a pointer amit a & művelet visszaad az külön létrejön a memóriaterületen? Vagy a fordító van annyira intelligens és fölöslegesen nem hoz létre újabb pointert hanem egyszerűen a "pointer" változó értékét írja át az adott címre?
A & által visszaadott pointer típusa pedig automatikusan olyan típusú, amilyen kifejezésben használták a & kifejezést?
Azért érdekel ennyire, mert a könyveim annyit írnak hogy "az objektum címét adja vissza", de ez nemsok minden.
Nagyon köszönöm a segítségedet, üdv
Kipróbáltam:
// compiler: gcc
#include <cstdlib>
#include <cstdio>
int main()
{
int x = -2;
int* pointer;
printf( "(1) A pointer valtozo memoriacime: %d \n", &pointer );
pointer = &x;
printf( "(2) A pointer valtozo memoriacime: %d \n", &pointer );
printf( "Az x valtozo memoriacime: %d \n", &x );
getchar();
return 0;
}
// eof
(1) A pointer valtozo memoriacime: 2293576
(2) A pointer valtozo memoriacime: 2293576
Az x valtozo memoriacime: 2293580
Mivel a & művelet pointert ad vissza logikusnak tűnt hogy úgymond "új pointert hoz létre" és azt adja vissza, a balérték pedig "felülíródik az új pointerrel", de mivel ugyanott maradt a memóriaterületen ezért a második verzió a helyes ugye? Tehát a pointer = &variable művelet a pointer értékét írja át.
Bocsi hogy túlbonyolítom :D
Kapcsolódó kérdések:
Minden jog fenntartva © 2024, www.gyakorikerdesek.hu
GYIK | Szabályzat | Jogi nyilatkozat | Adatvédelem | Cookie beállítások | WebMinute Kft. | Facebook | Kapcsolat: info(kukac)gyakorikerdesek.hu
Ha kifogással szeretne élni valamely tartalommal kapcsolatban, kérjük jelezze e-mailes elérhetőségünkön!