Új hozzászólás Aktív témák
-
kingabo
őstag
Hát ez "picit hosszú lett. Remélem érthető. Ha kérdésed van kérdezz nyugodtan, ha a többiek valami hibát találnak szóljanak és modikkal javíttatom.
A pointer az egy mutató a memória egy pontjára, vagyis egy memória címet tárol (egy int-et). Minden pointernek meg kell mondani, hogy milyen típusra mutat, hogy a fordító tudja, mi van ott: pl char vs float, mekkora (1byte vs 4byte). A pointert minden esetben használat elött inicializálni kell! (egyszerűbben: értéket kell neki adni), mivel a pointer "kezdeti értéke" nem NULL (==megegyezés szerint nem mutat sehova sem /semmire sem), hanem valamilyen memória szemét, ha ezt a szemetet akarod felhasználni arra, hogy elérd a pointer által mutatot memória helyet elég csúnya dolgok történhetnek ("jobb esetben" az oprendszer nem hagyja, "rosszabb esetben" felhasználhatják a program eltérítésére). Pointer deklarálása: a "mutatandó" érték típusa és a változó neve elé egy *:
int * p;Érték adás:
- NULL:
int *p = NULL;
- Egy másik pointer(1), vagy pl tömb esetén annak egy tetszőleges eleme(2) (részletesen lentebb)
int *pt,*pt1,t2[]={1,2,3,5};
pt = t2; //(2)
pt1 = pt; //(1)
pt = pt1 + 3; //(2) a t2 4. elemére mutat (1+3)- Egy változó címe: a váltózó elé a '&' jelet kell írni
int *p, i = 5;
p = &i;
- Dinamikusan foglalunk le memóriát (ld. késöbb)Pointer értéke vs pointer ÁLTAL MUTATOTT memória hely értéke: ezek keverése miatt lehet igen csúnya dolgokat (==hibákat) művelni. A pointer értéke egy memória cím, ami (jó esetben) egy megfelelő értékre mutat. A pointer által mutatott hely értéke az elején részletezett, a pointer deklarácójakor megadott típusú érték. Ezt elérni a pointer neve elé tett *-al lehet. Pl az előző részből az utolsó példában a 'p' pointer értéke az 'i' változó CÍME a memóriában. Az általa mutatott ÉRTÉK, vagyis a *p értéke 5 (*p = i = 5).
A gondot az okozhatja, hogy "véletlenül" lemarad a 'p' elől a *, így a pointer kap értéket, nem az általa mutatott memória cím. Vagyis ha az 'i'-nek értéket akarunk adni a 'p' pointeren keresztül akkor így kell *p = 6; // ==i;. A következő kód hibás, csak szemléltető ellenpélda:p = 6;, ekkor az 'i' értéke nem változik, a 'p' értéke egy, a programunktól független (nem a program egy változójára, vagy általa dinamikusan lefoglalt memória területre mutat), a 6-os memória címre fog mutatni, ahol biztosan semmi keresni valónk nincs és valószínűleg az oprendszer adatai vannak, vagyis semmi jóra ne számítsunk. (akit érdekel picit részletesebben itt az utolsó 2 sor)Egy dimenziós tömb: avagy a vektor. Egy 'n' elemű vektor, n db a memóriában egymás után lévő érték. (mint linalgból) Szemléletesen pl 3 elemű tömb (az '_' helyére kerülnek az értékek: |_|_|_|
Pl: int t[]={1,2,3,5};
A dolog szépsége az, hogy a 't' is egy pointer lesz, mely a tömb első elemének memóriabeli címére fog mutani.
A tömb elemeinek elérése
- a pointer eltolása, vagyis a pointer értékéhez hozzáadva, vagy levonva belőle. Fontos: nincs semmilyen védelem, hogy a pointer ne tudjon a tömbről "lemenni". Célszerűbb (főleg az elején), inkább a másik megoldást alkalmazni, kisebb a hiba lehetősége.
pl (a fenti t-t felhasználva): int *masodikElem = t+1; // a t 2. elemére mutat (1+1)
int harmadikElemErteke = *(t+2); //t eltolva 2-vel, vagyis a 3
. elem címére mutat és vesszük a címen lévő értéket
- a [] operátort használva. a változó után írva, a zárójelek közé egész számot, vagy egész típusú változót írva.Fontos: a megszokottól eltérően, c-ben az indexelés 0-tól n-1-ig történik! Vagyis pl, a zárójelek közé 0-át írva az általunk megszokott első elemet, n-1-et írva az általunk megszokott utolsó elemet kapjuk. További megjegyzendő tény, hogy nincs semmi védelem az ellen, hogy ne "menjünk le" a tombről. Más szóval ha a c-s indexeléssel nézve a -1-edik vagy az n-edik elemét akarjuk elérni a tömbnek (ami nem létezik), akkor a memóriában a tömb elött és utána lévő értékeket fogjuk elérni, amiről már a legelején megemlítettem, hogy mindent csak jót nem jelent! (Érdekesség: a fordító ezesetben átfordítja az előző változatra. pl t[2]-ből *(t+2) lesz)Dinamikus helyfoglalás: adott a probléma: fordítási időben meg kell adnunk, hogy mekkora lesz a tömbünk mérete. Viszont, ha ez az érték csak futási időben derül ki, mert pl a felhasználó fogja megadni, akkor dinamikusan, azaz futási időben kell a tömböt lefoglalni. Módja 1 dimes tömb esetén:
- 1 deklarálni egy megfelelő típusú pointert, ami majd a tömb első (azaz 0.) elemére fog mutatni
- malloc-kal lefoglani n db, a fenti pointer által mutatott típusnak megfelelő méretű, folytonos memória területet
- a pointer típusára konvertálni a malloc által visszaadott pointert (ha valakit érdekel miért kédezzen rá, de nem igazán ez a szint, szerintem)
pl:int *p, n = 0;
//... n értéket kap
p = (int*) malloc (sor * sizeof (int));
Ha pl int helyett float kell, akkor 3 (azaz három) helyen kell cserélni a típust: a pointer deklarációjánál, a sizeof paraméterében (ez adja meg, hogy mekkora helyet foglal 1 adott típusú változó), illetve a malloc elött.
Esetleg célszerű egy if ágban lekezelni, hogy sikeres volt-e a hely lefoglalása:
if ( (ppt = (int*) malloc (oszlop * sizeof (int)) ) == NULL)
{ printf ("nincshely");
exit (1);
}//if
A fenti kód megpróbál oszlopnyi int-et lefoglalni, ennek első elemre mutató pointerét értékül kapja a ppt. Ezt utána megvizsgálunk, hogy NULL-e (ez az ami nem mutat sehova, vagyis nem sikerült lefoglalni), ha NULL, akkor a hibáról értesítjük a felhasználót és 1-es hibakóddal termináljuk (befejezzük) a program futását.
Memória felszabadítása: az általunk malloc-kal lefoglalt memóriát senki sem fogja felszabadítani, így ha már nincs rá szükségünk, vagy ugyanazt a pointert akarjuk használni egy újabb terület lefoglalásához, akkor elötte kötelező felszabadítani a memória területet, illetve célszerű a pointernek a NULL értéket adni. Ha az elöbbit elfelejtjük a programunk elszívárogtatja a memóriát (memoryleak). A felhasználó ebből azt veszi észre, hogy minnél többször futtatja a programunkat, annál több szabad hely fogy el, amit a program lefutása után nem kap vissza. A második inkább csak biztosági szempontok miatt szükséges, nehogy valaki véletlenül (esetleg rossz akaratúak direkt) a már felszabadított memória területről próbáljanak olvasni, esetleg írni (amivel más adatai írodnak felül) Fontos: NE felejtsük el felszabadítani a lefoglalt területet!
Felszabadítás: free(pointer); //ahol a pointer egy dinamukusan lefoglalt
memória terület kezdőcíme
Tényleg ne maradjon le az a free! Nem lehet elég sokszor hangsúlyozni.Több dimenziós tömbök: az egyszerűség kedvéért csak 2 dimes tömb, de ez alapján könnyen megoldható magasabb dimszámra is. (ugyanazt kell elvégezni még annyiszor, amíg el nem érjük a kívánt dim-et, ami alább következik)
Megvalósítás(elmélet): 2 dimes tömböt c-ben csak úgy tudunk megvalósítani, hogy létrehozunk egy tömböt, amely minden eleme a 2 dimes tömb egy sorára mutat.
Pl: 3x4-es tömb: van egy 3 elemű tömbünk, amelynek minden eleme egy-egy 4 elemű tömb első elemére mutat. Szemléletesen (a mutatást a -> jelőli)
_
|_| --------------> |_|_|_|_| első sor
|_| --------------> |_|_|_|_| második sor
|_| --------------> |_|_|_|_| harmadik sorTömbben lévő értékek elérése: mesélve: először (a fenti 3x4-es pl-nél maradva) a 3 elemű tömbben kell megkeresni a kiválaszotott sort tartalmazó tömb címét. Ezután elugrani erre a címre és a megadott oszlop elemének címét meghatározni.
Konkrét pl-en: próbáljuk meghatározni a fenti tömb (szokásos indexeléssel) a 3. sor 2. oszlopában lévő elemet. Ekkor először meg kell tudnunk a 3. sort tartalmazó vektor címét, vagyis t[2]-t (==t[3-1]). Ezután már csak a 2. oszlop elemét kell venni, vagyis t[2][1] (ld. előbb). Megvalósítás a pointer eltolásos technikával, inkább csak érdekesség képpen: a 3. sor címe: *(t + 2), az így megkapott vektor a 2. elemének értéke: *( *(t + 2) + 1)
2 dimes tömb lefoglalása: a fenti szemléltető ábrán is (remélhetőleg) jól látszik, hogy 4 (azaz négy) darab tömböt kell lefoglalnunk, melyek a memóriában "bárhol" lehetnek (az össz megkötés annyi, hogy a tömb elemei egymás után jönnek):
- 3db 4 elemű tömböt (vagyis lesz 3db pointerünk)
- 1db 3 elemű pointereket tartalmazó tömböt (vagyis ennek típusa pointereket tartalmató tömb, ami viszont szintén egy pointer, vagyis 2db csillag kell deklarációnál)
Pl:
//2 dimes tömb lefoglalása
int **ppt, sor, oszlop, i,j;
//.. értékadás a sor-nak és az oszlop-nak
//első rész lefoglaljuk az oszlop vektort, ami majd tárolja a
sorok címeit
if ( (ppt = (int**) malloc (sor * sizeof (int*)) ) == NULL)
{ printf("nincshely");
exit(1);
}//if
//második rész lefoglaljuk egyesével a sorokat
for (i = 0; i < sor; i++)
if ((ppt[i] = (int*) malloc (oszlop * sizeof (int)) ) == NULL)
{ printf ("nincshely");
exit (1);
}//if
//for//2 dimes tömb bejárása:
for(i=0;i<sor;i++)
{
for(j=0;j<oszlop;j++)
{
ppt[i][j]=j; //itt tetszüleges int állhat; értékadás
printf("%d ",ppt[i][j]); //érték "lekérése"
}//forj
printf("\n");
} //fori//2dimes tömb felszabadítása:
for(i = 0; i < sor; i++)
free(ppt[i]);
free(ppt);Pont úgy ahogy a fenti pl-nél maradva 4 tömböt kell lefoglalni 4-et is kell felszabadítani!
2 dimes tömb megvalósítása 1 dimes tömbbel: a 2 dimes tömb felfogható úgy is, hogy egymás után írjuk a sorait, így egy 1 dimes tömböt kapunk. A korábbi 3x4-es pl, így egy 1x12-es vektorra módosul. Egyetlen "probléma", hogy hogyan lehet kiszámolni a harmadik sor második elemének indexét. A megoldás nagyon egyszerű, csak végig kell gondolni mi történt az eredeti tömbbel (a szokásos indexelést használva): leírtunk egymás után 2 sort (2 * 4 elemet), majd a 3. sort megkezdve leírtunk még 2 elemet, vagyis meg is lett a 3.sor 2. eleme. (persze a többit is leírtuk, de az index kiszámításához nincs rájuk szükség) Vagyis mostmár általánosan: az x-edik sorhoz elötte el kell mennünk x-1 sor elemein, majd az x. sor elemei közül még az y-adikig. Legyen mondjuk minden sorban n db elem, ekkor az x-edik sor y-adik eleme: (x - 1) * n + y. Már csak azt kell végig gondolni, hogy a c féle 0-tól való indexelés változtat-e valamit vagy sem. Vagyis, ha a (innentől már a c-s indexelés) 0. sor 1. (ami nálunk az 1 sor 2. eleme) elemének az indexét kell meghatároznunk, akkor az 0 * n + 1 indexű lesz, ha az 1. sor 3. eleme 1 * n + 3, vagyis általánosan x-edik sor y-adik eleme: x * n + y.
Magasabb szinten, esetleg optimalizáláskor jöhet jól, ugyanis ezzel a módszerrel a tömb tetszőleges eleme 1 lépésben megkapható, míg a korábban leírt változattal csak 2 lépésben
Több dimenziós tömbök: csak vázlatosan: vizuális típusú embereknek: téglapból (XxY) úgy lehet téglát(XxYxZ) készíteni, hogy az (x,y) koordináta "fölé is teszünk 1 vektort(magasság)". Ezesetben ugyanúgy lesz 1 tömbünk, ami a sorokra mutató pointereket fog tartalmazni, viszont a sorok most szintén pointereket fognak tartalmazni, ezek lesznek az (x,y) helyen a fölé tett vektorra mutató pointerek. Vagyis lesz 1db X elemű tömb(int***), amiben lesznek a sorokra mutató pointerek (int**), lesz Xdb Y elemű vektor(int**), amik a magasság vektorokra mutató pointereket (int*) tartalmaznak és lesz X*Ydb Z elemű vektor (int*), ami tartalmazza a magasság adatokat (int).
Másként lehet úgy is nézni, hogy van egy vektorunk, aminek minden elem 1-1 két dimes tömbre mutat. Magasabb dim-ek ezzel a rekurzióval előállíthatók, vagy alkalmazható a fenti dim csökkentő módszer, bonyolultabb képlettel.Láncolt listák: mese: aki érti a 2 dimes tömböknél történteket, annak ez sem jelenthet gondot. A láncolt lista azt az ötletet használja fel, amit a sorok címének meghatározására használtunk fel. Viszont itt azt szeretnénk, hogy tárolhassunk a pointer mellett még adato(ka)t, illetve ne egy sorra mutasson a pointer, hanem egy másik ugyanilyen elemre. Ilyen adatszerkezetet használnak, akkor ha pl nem tudjuk előre, hogy mennyi adat lesz és a felhasználó sem tudja előre. Béna példa: mennyi madarfajt tudsz felsorolni? Ezt a példát tovább víve, a listában tárolandó adat a madárfaj neve, a pointer értéke kezdetben NULL, aztán ha a felhasználó megad egy újabb madárfajt, akkor dinamikusan létrehozunk még egy ilyen lista elemet, és ennek címét állítjuk be az előbb említett pointernek. Célszerű egy külön pointerben eltárolni a lista első elemét (fej/fej elem), és bejáráskor egy másik pointerrel (akt) végig menni rajtuk. (így nem veszik el az eleje)
Megvalósítás: szükséges hozzá tudni mi az a struct! A lista típusa egy struct lesz, ami lehetővé teszi, hogy adatokat is tárolhassunk és mutathassunk egy másik azonos típusú struct-ra.
Saját structunk létrehozása, illetve a fej deklarálása:
typedef struct madarFaj
{
char nev[30];
struct madarFaj *kov; // kov pointer madarFaj típusú
struktúrára
}MADARFAJ; //létrehoztuk a MADARFAJ nevű típust
MADARFAJ *fej, *akt;
char madarFajNeve[30];Új létrehozása és értékadások:
//ciklusban madárfaj bekérése
MADARFAJ p = (MADARFAJ*) malloc (sizeof (MADARFAJ) );
//új listaelem lefoglalása
p->nev = madarFajNeve; //elmentjük a faj nevét
p->kov = NULL; //még nincs következő lista elem, ezért nem mutat sehova sem
akt->kov = p; //hozzá fűzzük a listához az új elemet.Fontos: mivel a lista elemei is dinamikusan lefoglalt memória terültetek, ezért egyesével végig kell menni minden lista elemen és fölszabadítani!
akt = fej;
while (akt != NULL) //amíg van elem a listában
{
MADARFAJ *p = akt; //elmentjük az aktuális listaelem címét
akt = p->kov; //az aktot a következő elemre állítjuk
(felszabadítás után nehéz lenne)
free(p);
} -
kingabo
őstag
A pointerezést holnap megpróbálom összeírni, meg a "mágiás" részt. már ha elfogadod tőlem, nem írja meg elöbb senki sem, nem google-zol rá
"Ja a lényeg kimaradt: a zh példában ha jól emlékszem 8x8-as tömb volt megadva."
Ez esetben a progi elejére:#define MAX_MERET 8 és tömb deklarációnál ciklusoknál MAX_MERET-et írod be. C-ben nincs const, csak így lehet megoldani, a precompiler fogja minden egyes helyen behelyettesíteni a kódban a MAX_MERET helyére a 8-at. Azért célszerű így csinálni, mert ha valaki azt mondja, hogy mégse 8 hanem 10 elemű a tömb, akkor 1 helyen átírod, újrafordítod és kész is vagy, nem kell mindenütt cserélgetni.aztán meg debuggolni ha valahol mégis kimaradt. A keres + csere működhet, ha a kódban nincs pl 128, de ha van, máris nem lehet keres+cserét használni"Az én hibám hogy AxB-t írtam, de ha kukacoskodni akarnék nem kötöttem ki hogy A!=B
"
Viszont azt se írtad, hogy A==B, ezért vettem úgy hogy nem egyenlők.
Nem bme-s vagyok, nem tudom ott hogy oktatják/nem oktatják a programozás. Számomra teljesen logikus volt, hogy AxB-sre ha nem megy a dolog, akkor megszorítom a kisebb dim szerinti négyzetes mátrixra...
Új hozzászólás Aktív témák
● olvasd el a téma összefoglalót!
● ha kódot szúrsz be, használd a PROGRAMKÓD formázási funkciót!
- Új DELL Inspiron 16 Fémházas Multimédiás Laptop 16" -40% Ryzen 7 8840U 8mag 16/1TB FHD+ IPS
- Sony FE 28-70 mm F3.5-5.6 OSS
- PS5 Slim Disc 1TB 2026.10.08 GARANCIA
- Bomba Ár! Dell Latitude 3190 - Intel N4120 I 4GB I 128GB SSD I 11,6" HD I Cam I W11 I Garancia!
- Bomba Ár! Dell Latitude 3190 - Intel N4120 I 4GB I 64GB SSD I 11,6" HD I Cam I W11 I Garancia!
- AKCIÓ! Gigabyte B760M i5 14600KF 32GB DDR4 512GB SSD RX 6800XT 16GB Rampage SHIVA CM 750W
- BESZÁMÍTÁS! VALVE Steam Deck LCD 1TB SSD kézikonzol garanciával hibátlan működéssel
- BESZÁMÍTÁS! Asus H110M-K i7 6700 16GB DDR4 480GB SSD GTX 1660Ti 6GB Rampage SHIVA FSP 550W
- BLUESUMMERS NVMe SSD adapter
- AKCIÓ! Gigabyte B85-HD3 B85 chipset alaplap garanciával hibátlan működéssel
Állásajánlatok
Cég: CAMERA-PRO Hungary Kft
Város: Budapest
Cég: PCMENTOR SZERVIZ KFT.
Város: Budapest