8. gyakorlat¶
5. ZH¶
A gyakorlat elején 40 percben kerül megírásra az 5. zh. A feladatok témái:
- algoritmus elkészítése
- függvények megírása, bemenet/kimenet kezelés nélkül
- tömbökön végzett műveletek
- a
struct
típus használata
Pointerek¶
Új dinamikus változó létesítése¶
1 |
|
A malloc(S)
függvény lefoglal egy S
méretű memóriaterületet a program számára.
A sizeof(E)
megadja, hogy egy E
típusú változó mekkora helyet igényel a memóriában.
A malloc(sizeof(E))
hatására tehát létrejön egy új E
típusú érték tárolására (is) alkalmas változó, és ez a változó lesz a p
értéke.
Pointer dereferencia¶
1 |
|
A *
művelet segítségével érhetjük el a p
értékét, vagyis a dinamikus változót.
A *p
változóhivatkozás a p
értékére, vagyis a dinamikus változóra hivatkozik, tehát a *p
értéke a dinamikus változó értéke lesz.
Dinamikus változó törlése¶
1 |
|
A művelet hatására a p
-hez tartozó memóriaterület felszabadul, ezáltal a dinamikus változó megszűnik.
A művelet végrehajtása után a p
pointerhez nem tartozik érvényes változó, ezért a *p
változóhivatkozás végrehajtása jobb esetben azonnali futási hibát eredményez.
(Rosszabb esetben pedig olyan lappangó hibát, aminek az eredménye a program egy teljesen más pontján jelenik meg.)
A címképző művelet¶
1 |
|
A művelet meghatározza egy változóhoz tartozó memória mező címét.
Ha egy p
pointer típusú változó értéke az i
változóhoz tartozó memória címe, akkor azt mondjuk, hogy a p
i
-re mutat.
A pointerek megértésében segíthetnek az alábbi videók is:
Feladat (f0215)
Feladat:
Futtasd le a cim.c
programot, és értelmezd!
Melyik érték melyik értékkel egyenlő, és miért?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Válaszok
- A
pa
és&a
értékek egyenlőek, mert a 14. sor miatt apa
aza
változó címét tartalmazza, apa
aza
változóra mutat. - Az
a
és*pa
értéke megegyezik, hiszen apa
aza
változóra mutat, tehát apa
által referált változó értéke aza
változó értéke.
Feladat (f0216)
Feladat:
Javítsd ki a csere.c
program csere függvényét (és a hozzá tartozó hívást) úgy,
hogy megcserélje a main
két változójának értékét.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Ötlet (f0216)
A probléma az, hogy a függvény a két argumentum értéket veszi át, ezt eltárolja az x
és y
paraméterekben, majd a (technikailag lokális változóként működő) x
és y
paraméterek értékét cseréli meg.
Ha e helyett a függvény azt kérné el, hogy hol van a memóriában ez a két érték, és ennek a két memóriaterületnek az értékét cserélné meg, akkor valójában a main
függvény x
és y
változójának értékét cserélné fel.
Vagyis a függvénynek két memóriacímet (pointert) kellene átvennie paraméterként, és híváskor a main
két változójának a címét kellene átadni.
A függvénynek a két pointer által dereferált értéket kellene megcserélnie.
Lehetséges megoldás (m0216.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
A fenti feladattól eltérő, a változók cseréjéről készült kapcsolódó videó:
Feladat (f0214)
Feladat:
Elemezd és futtasd a pointerek.c
programot.
Mi a különbség p
, q
, illetve *p
és *q
értéke között?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
Válaszok (f0214)
p
ésq
két pointer, két memóriacím, amik egyszer a 16. és 17. sorban kapnak különböző értékeket, majd a 26. sortól megegyeznek.*p
és*q
ap
ésq
változók által mutatott, a 16. és 17. sorban amalloc()
segítségével létrehozott két különböző dinamikus változó, legalábbis a 25. sorig. A 25. sorban az addig*p
-vel hivatkozott, 16. sorban létrehozott dinamikus változót megszüntetjük, majd a 26. sortól az értékadás miatt a*p
és*q
ugyanazt a dinamikus változót jelöli.- Vagyis a 26. sorig a
*p
és*q
két különböző (dinamikus) változó(-ra hivatkozik), a 26. sortól pedig a*p
és*q
ugyanaz az egy (dinamikus) változó (ugyanarra hivatkozik).
Feladat (f0191)
Feladat:
Egészítsd ki a malloc-free.c
programot úgy, hogy a végrehajtása során ne
keletkezzen futási hiba!
- A 12. sorban inicializáld a változókat!
- A 14. sorba szúrj be egy utasítást!
- A kettő közül melyik a helyes megoldás? A másik (önmagában) miért nem jó?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Lehetséges megoldás (m0191-1.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Lehetséges megoldás (m0191-2.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Válaszok (f0191)
A kettő közül a második verzió, azaz a q = p;
utasítás beszúrása a 14. sorba a helyes.
Az első verzióval az a baj, hogy bár a free(q)
hívás NULL
pointert kap, és a free()
függvény NULL
pointer argumentum esetén nem dob hibát, a p
által mutatott memóriaterület nem lesz felszabadítva.
Megjegyzés
Az igazán szép és helyes megoldás, ha mindkét előző "korrekciót" egyszerre alkalmazzuk, vagy is a két változó inicializálása mellett szúrjuk be a q = p;
utasítást a 14. sorba.
A malloc
-ot több elem (tömbök) egyszerre történő lefoglalásához is használhatjuk.
Amikor egy tömbnek ilyen módon foglalunk memóriát, akkor azt mondjuk, hogy a tömb egy dinamikus tömb.
A dinamikus tömbökről az alábbi videó ad részletes magyarázatot:
Azt már lehet sejteni, hogy a tömbök és a pointerek igen szoros kapcsolatban állnak egymással. Ezt a szoros kapcsolatot magyarázza el az alábbi videó:
Feladat (f0217)
Feladat:
-
Írj egy programot, amelyben deklarálj egy 10 elemű
int
tömböt, majd töltsd fel értékekkel a standard inputról! Írasd ki a tömb elemeit. -
Az előző programban deklarálj egy
int
pointert is, és a beolvasást ennek segítségével valósítsd meg! -
Most az első tömbelem értéke előtt kérd be a tömb méretét (a beolvasandó elemek számát). Mi történik, ha 10-nél kisebb értéket, 10-et, 10-nél nem sokkal nagyobb értéket, illetve 10-nél sokkal nagyobb értéket adsz meg elemszámként?
-
Ezután töröld a tömb deklarációját és az azonosítóját pointerként deklaráld! Az elemszám megadása után de a tömbelemek bekérése előtt dinamikusan foglalj helyet a tömb elemei számára (pontosan annyit, amennyi kell)! Most mi történik, ha tömbméretnek különböző értékeket adsz meg?
Lehetséges megoldás (m0217-1.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Ötlet (f0217/2)
Ha egy pointer egy tömb valamelyik elemére mutat, akkor a pointer eggyel növelt értéke a tömb következő elemére fog mutatni.
(Általában a pointer i-vel növelt értéke a tömb pointer által mutatott elemétől számolt i. elemre fog mutatni.)
A scanf
egy memóriacímet vár, ahová eltárolhatja a beolvasott értéket (ezért kellett a változók elé az &
jel); a pointer értéke pedig önmagában egy memóriacím.
Vagyis, ha a beolvasó ciklus elején a pointer a tömb első elemére mutat, akkor a pointert minden ciklus végén eggyel-eggyel növelve az szépen végigmegy az összes tömbelemen.
A scanf
-nek pedig ezt a pointer értéket kell átadni, hiszen ez mutat arra a tömbelemre, ahová az éppen beolvasott értéke el kell tárolni.
Lehetséges megoldás (m0217-2.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Lehetséges megoldás (m0217-3.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Válaszok (f0217/3))
- 10-nél nem nagyobb érték esetén nincs probléma: legfeljebb annyi történik, hogy a program nem használja fel a tömb összes elemét, így azok feleslegesen lesznek lefoglalva, de ez a program futásában nem okoz problémát.
- 10-nél kicsit nagyobb érték megadása lehet, hogy furcsa viselkedést idéz elő a programban. Működhet látszólag jól, helyesen, esetleg rendellenes viselkedést tapasztalhatunk (pl. több elemet olvas be, kevesebbet ír ki) vagy látszólag helyes működés mellett kiírhat stack hiba miatti leállást ("stack smashing detected").
- 10-nél sokkal (nagyságrendekkel) nagyobb szám esetén simán memóriahibával ("Segmentation fault") elszáll.
Ötlet (f0217/4)
Használd a malloc
függvényt, és ne feledkezz meg a memória felszabadításáról (free
) sem!
Lehetséges megoldás (m0217-4.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Válaszok (f0217/4))
- A program bármekkora (a gép fizikai korlátai által megengedett) pozitív elemszámra helyesen működik.
Feladat (f0221)
Feladat:
Vizsgáld meg a tombbejaras.c
programot.
Mi történik, ha a három konstans (N
, M
és K
) értékét megváltoztatod?
- Vedd
N
értékét valamivel nagyobbra. - Vedd
M
értékét valamivel nagyobbra. - Vedd
K
értékét valamivel nagyobbra. - Vedd
K
értékét valamivel kisebbre.
Mely esetekben hogyan viselkedik a program a futás során? Mi az oka a tapasztalt viselkedésnek?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
Válaszok (f0221)
- Ha több sor van (
N
-t növeljük), akkor a tömb sorfolytonosan kerül feltöltésre, az utolsó elemek értékei véletlenszerűek lesznek. - Ha több sor van (
M
-et növeljük), akkor a tömb sorfolytonosan kerül feltöltésre, az utolsó elemek értékei véletlenszerűek lesznek. - Ha több elemet töltünk fel, mint amekkora a tömb (
K
-t növeljük), akkor a program kiszámíthatatlan módon működik. Elszállhat memóriahibával, furcsán viselkedhet, de az is lehet, hogy nem tapasztalunk semmi különöset (haK
-t csak kicsivel növeljük). - Ha kevesebb elemet töltünk fel, mint amekkora a tömb (
K
-t növeljük), akkor a tömb sorfolytonosan kerül feltöltésre, az utolsó elemek értékei véletlenszerűek lesznek.
Fun feladat¶
Feladat
Készíts egy programot, amely bekér egy magasságot illetve egy karaktert, majd kirajzol egy ilyen magas piramist, az így megadott karakterből.
Példa:
1 2 3 4 5 6 7 |
|
Lehetséges megoldás
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
Challenge - Matrix Code Rain¶
Feladat
Készítsünk egy programot, mely a "Mátrix" című filmben látható "code rain"-t valósítja meg!
Figyelem!
Ez a feladat már túlmutat a törzsanyagon, így ennek elkészítését csak azoknak ajánljuk, akik már megbirkóztak az egyszerűbb pointer-es feladatokkal.
Feladatok¶
Feladat
Írj függvényt, ami egy tömböt és a tömbben lévő elemek számát várja paraméterül, visszatérési érték nincs. Bekérünk a felhasználótól egy pozíciót és egy értéket, és elhelyezi az értéket az adott pozíción a tömbben. A függvényt a main függvényen belül hívd meg, miután feltöltötted a tömböt tetszésed szerint. (A tömb mérete mindenképpen nagyobb kell, hogy legyen a híváskor benne lévő elemek számánál, hogy tudjunk még új elemeket beszúrni).
Példák:
-
példa:
- A tömb:
[2, 5, 7, 45]
- Input:
2 1000
- Output:
[2, 5, 1000, 7, 45]
- A tömb:
-
példa:
- A tömb:
[2, 5, 1000, 7, 45]
- Input: 0 55
- Output:
[55, 2, 5, 1000, 7, 45]
- A tömb:
Tehát az adott pozícióra (0-tól indexszelünk) az új érték kerül és minden elem eggyel arrább tolódik.
Figyelj!
Figyelj arra, hogy a tömb mérete elég legyen ahhoz, hogy a beszúrt elemek is elférjenek benne!
Lehetséges megoldás
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
Feladat
Írj függvényt, aminek a bemeneti paramétere egy karaktertömb, visszatérési értéke a karaktertömb hossza a feladat elvégzése után. A függvényen belül:
-
Cseréld ki a kapott tömb összes kisbetűjét nagybetűre, a nagybetűket pedig kisbetűre. Példa:
- Input:
"Bela nem hajlando Dezso nelkul Las Vegasba menni."
- Output:
"bELA NEM HAJLANDO dEZSO NELKUL lAS vEGASBA MENNI."
- Input:
-
Vedd ki az összes helyközt a karaktertömbből. Példa:
- Input:
"Bela elment megkapalni a lekvarfoldet."
- Output:
"Belaelmentmegkapalnialekvarfoldet."
- Input:
A változtatásokat a kapott karaktertömbben is végezzük el, ne csak kiíratás során végezzük el a változtatásokat. Ezt a függvényt a main függvényen belül hívd meg, miután feltöltötted a karaktertömböt tetszésed szerint.
Feladat (f0218)
Feladat:
Olvass be 5 darab maximum 99 karakter hosszú szót úgy, hogy mindegyiknek pontosan annyi helyet foglalsz, amennyi kell! A sztringeket írasd ki, majd szabadítsd fel a lefoglalt területet! (Természetesen beolvasáskor használhatsz egy átmeneti tárolót.)
Feladat
Írj egy programot, melyben egy függvény képes a paraméterben megkapott sztring megfordítására!
A függvény tisztán pointer aritmetikával dolgozzon, azaz ne használj benne tömbindex operátort ([]
)!
Feladat (f0228)
Problémafelvetés:
Számítsuk ki egy mátrix transzponáltját.
Specifikáció:
A program inputja egy mátrix, (n,m)[a_11,...,a_1m,a_21,...,a_n1,...,a_nm]
alakban, ahol n
a sorok, m
az oszlopok száma (egész számok), a_ij
pedig
valós szám, a mátrix i. sorának j. eleme, (azaz a mátrix elemeit
sorfolytonosan adjuk meg).
A program outputja a transzponált mátrix az inputhoz hasonló alakban.
Algoritmustervezés:
Dinamikus memóriafoglalással dolgozzunk. A transzponálást valóban végezzük el, ne csak a kiíratás során.
Feladat (f0230)
Problémafelvetés:
Egy számsorozat negatív és nemnegatív elemeit kell szétválogatni.
Specifikáció:
A program inputja egy A számsorozat (n)[a_1,...,a_n]
alakban megadva.
A kimenet első sora az A negatív elemeiből, a második sora az A nemnegatív
elemeiből képzett számsorozat, az inputhoz hasonló formában megadva. A két
részsorozatban az elemek sorrendje megegyezik az eredeti sorozatban megadott
sorrendjükkel.
Algoritmustervezés:
Dinamikus memóriafoglalással dolgozzunk. A két részsorozatot valóban állítsuk elő, ne csak a kiíratás során.
Feladat (f0265)
Problémafelvetés:
Készíts egy programot, amely kiszámolja két valós vektor skalárszorzatát.
Specifikáció:
A program inputja egy A és egy B vektor, (n)[a_1,...,a_n]
illetve
(m)[b_1,...,b_m]
alakban, külön sorban megadva.
A program kimenete egyetlen szám, a két input vektor skalárszorzata, vagy a
"HIBA"
sztring, ha a két vektor nem összeszorozható. A kimenetet egy sorvége
jel zárja.
Algoritmustervezés/Megvalósítás:
Dinamikus memóriafoglalással dolgozzunk.
Segítség
Egy \(A=(a_1, a_2, ..., a_n)\) és egy \(B=(b_1, b_2, ..., b_n)\) vektor skalárszorzata a \(\sum_{i=1}^{n}a_ib_i\) szám.
Részletesen lásd: Wikipedia
Feladat (f0268)
Problémafelvetés:
Készíts egy programot, amely beolvas N egész számot, eltárolja őket egy tömbben, majd a tömböt úgy alakítja át, hogy a sorban egymás után ismétlődő elemek közül csak egyet hagy meg.
Specifikáció:
A program inputjának első eleme egy N egész szám, majd N darab további egész szám. A program kimenete az input számsorozat de úgy, hogy az egymást közvetlenül követő azonos értékű elemek közül csak egy marad meg. A kimenetben a számok egy-egy szóközzel vannak elválasztva, a sort pedig egy sorvége jel zárja.
Algoritmustervezés/Megvalósítás:
Dinamikus memóriafoglalással dolgozzunk.
További gyakorló feladatok¶
Feladat (f0196)
Problémafelvetés:
Írj egy programot, ami megmondja, hogy egy csoport hallgatói teljesítették-e a félévet programozás alapjai gyakorlatból.
Algoritmustervezés/Megvalósítás:
A főprogram csak az input/output műveleteket végezze, a számolást külön függvény(ek)ben oldd meg. Készíts egy adatszerkezetet, amiben egy hallgató nevét, pontjait tároljuk. A program tároljon külön minden részpontszámot és a hiányzásokat is. A program először kérje be a hallgatók számát, majd foglaljon memóriát az adataik számára, és ezzel a dinamikus tömbbel dolgozzon.
Feladat (f0197)
Problémafelvetés:
Írj egy programot, ami megmondja, hogy egy csoport hallgatói teljesítették-e a félévet programozás alapjai gyakorlatból.
Algoritmustervezés/Megvalósítás:
A főprogram csak az input/output műveleteket végezze, a számolást külön függvény(ek)ben oldd meg. Készíts egy adatszerkezetet, amiben egy hallgató nevét, pontjait tároljuk. A program tároljon külön minden részpontszámot és a hiányzásokat is. A program először kérje be a hallgatók számát, majd foglaljon memóriát az adataik számára, és ezzel a dinamikus tömbbel dolgozzon. A program a "bemenet.txt" nevű fájlból olvassa be az adatokat és a "kimenet.txt" nevű fájlba írja ki az eredményt.
Feladat (f0211)
Problémafelvetés:
Adottak síkidomok (kör, elipszis, háromszög, négyzet, téglalap) a jellemző értékeikkel. Készíts egy tömböt ilyen alakzatok tárolására, olvasd be az adataikat, majd írd ki az összterületüket. Ezután írd ki mindegyik fajtából a legnagyobb területű síkidom kerületét.
Algoritmustervezés/Megvalósítás:
A főprogram csak az input/output műveleteket végezze, a számolást külön
függvény(ek)ben oldd meg. Az adatok tárolására használj összetett
adatszerkezetet, ha van értelme. A program a bemenet.txt
nevű fájlból
olvassa be az adatokat és a kimenet.txt
nevű fájlba írja ki az eredményt.
Feladat (f0229)
Problémafelvetés:
Számítsuk ki két vektor tenzorszorzatát.
Specifikáció:
A program inputja egy A és egy B vektor, (n)[a_1,...,a_n]
illetve
(m)[b_1,...,b_m]
alakban, külön sorban megadva.
A kimenet egyetlen sor, ami az A és B vektor tenzorszorzatát (az A mint oszlopvektor
és B mint sorvektor mátrixszorzatát) tartalmazza (n,m)[c_11,...,c_1m,c_21,...,c_n1,...,c_nm]
alakban (azaz a mátrix elemeit sorfolytonosan kiírva).
Algoritmustervezés:
Dinamikus memóriafoglalással dolgozzunk. A műveleteket valóban végezzük el, ne csak a kiíratás során.
Segítség
Egy \(A=(a_1, a_2, ..., a_n)\) és egy \(B=(b_1, b_2, ..., b_m)\) tenzori (didaktikus) szorzata az a \(C\), \(n \times m\)-es mátrix, amelynek elemei a \(c_{i,j}=a_ib_j\) képlettel kaphatóak meg. (Ha az \(A\)-t mint sorvektort egy \(n \times 1\)-es mátrixként, a \(B\)-t pedig mint oszlopvektort egy \(1 \times m\)-es mátrixként fogjuk fel, akkor ezen két mátrix mátrixszorzatára vagyunk kíváncsiak.)
Részletesen lásd: Wikipedia
Feladat (f0266)
Problémafelvetés:
Készíts egy programot, amely kiszámolja két valós vektor összegét.
Specifikáció:
A program inputja egy A és egy B vektor, (n)[a_1,...,a_n]
illetve
(m)[b_1,...,b_m]
alakban, külön sorban megadva.
A program kimenete egyetlen sor, melyben az összegvektor van a bemeneti
vektorok formájában kiírva, vagy a "HIBA"
sztring, ha a két vektor nem
összeadható. A kimenetet egy sorvége jel zárja.
Algoritmustervezés/Megvalósítás:
Dinamikus memóriafoglalással dolgozzunk.
Segítség
Egy \(A=(a_1, a_2, ..., a_n)\) és egy \(B=(b_1, b_2, ..., b_n)\) vektor összege a \(C=(a_1+b_1, a_2+b_2, ..., a_n+b_n)\) vektor.
Részletesen lásd: Wikipedia
Feladat (f0267)
Problémafelvetés:
Készíts egy programot, amely kiszámolja N dimenziós vektorok sorozatának összegét. A program először bekéri az N értéket, majd a vektorokat. A vektorok a végpontjaik valós koordinátáival adottak. A sorozat végét a nulla vektor jelzi.
Specifikáció:
A program inputjának első eleme egy egész szám (N), majd ezt követi I darab N elemű, valós számból álló számsor, ahol I legalább 1, az utolsó N elemű számsorban minden szám nulla értékű, de előtte minden N elemű számsorban van 0-tól különböző érték.
Algoritmustervezés/Megvalósítás:
Dinamikus memóriafoglalással dolgozzunk.
Kapcsolódó linkek¶
- Wiki: vektorok
- Videó - Pointerek bevezetés
- Videó - Pointerek: változóértékek cseréje
- Videó - Malloc
- Videó - Free
- Videó - Dinamikus tömb
- Videó - Tömbök és pointerek kapcsolata
- Videó - String megfordítása pointerekkel
- Videó - Dinamikus tömbfoglalás 1D és 2D
- Videó - Mátrix Code Rain