Gábor LÉKÓ, PhD student
Programozás alapjai gyakorlat 2017-2018/1
Az alap kurzusrész tematikája: * Programozási alapfogalmak: számítási probléma, algoritmus, program. * A C fejlesztő környezetek. A forrásprogram fordításának folyamata. * A programozás fázisai: problémafelvetés, specifikáció, algoritmustervezés, megvalósítás, költségelemzés, tesztelés, végrehajtás, fenntartás. * Vezérlési módok. Szerkezeti ábra fogalma. * Szekvenciális vezérlés és megvalósítása C nyelven. * Adattípus és változó fogalma. * A C programozási nyelv alapjai, elemi adattípusai. * Kifejezés felépítése és kiértékelése. Logikai kifejezés. * Beviteli (input) és kiviteli (output) utasítások. * Egyszerű C program szerkezete. * Szelekciós vezérlések (egyszerű, többszörös, esetkiválasztásos) és megvalósítása C nyelven. * Ismétléses vezérlések (kezdőfeltételes, végfeltételes, számlálásos, hurok, diszkrét) és megvalósítása C nyelven. * Eljárásvezérlés, függvényművelet és megvalósítása C nyelven. * Egyszerű rekurzió. * Blokkstruktúra a C nyelven. * Folyamatábra, szabályos folyamatábra, kapcsolat a szerkezeti ábrával. * Adattípusok, absztrakt adattípus. * Elemi adattípusok, összetett adattípusok, típusképzések a C nyelven. * Pointer típus, pointeraritmetika. * A kimenő és a be- és kimenő argumentumok kezelése. * Dinamikus változók. Memória modell. Modulok. * Tömb típus, pointerek és tömbök kapcsolata. * String típus és megvalósítása C nyelven. * Szorzat-rekord típus és megvalósítása C nyelven. * Az egyesített-rekord típus megvalósítása C nyelven. * Függvényre mutató pointer. * Bonyolultabb deklarációk. * Típuskényszerítés. * A parancssorban lévő argumentumok kezelése. * Az I/O alapjai. Formatált I/O műveletek. Hozzáférés az adatállományokhoz. * Alacsony szintű I/O. * A C előfeldolgozó: makrók, feltételes fordítás. Ajánlott irodalom: * Brian W Kernighan and Dennis M Ritchie, A C programozási nyelv, Műszaki Kiadó, 1985. * Brian W Kernighan and Dennis M Ritchie, A C programozási nyelv, Az ANSI szerint szabványosított változat, Műszaki Kiadó, 1996 * Benkő Tiborné, Benkő László, Tóth Bertalan, Programozzunk C nyelven, ComputerBooks, 1998. * Herbert, Schildt: C/C++ Referenciakönyv. Bp. : Panem, 1998 * Andrew Koenig: C csapdák és buktatók, Kiskapu Kft. 2005. * Juhász István, Kósa Márk, Pánovics János: C példatár, Panem, 2005
Gyakorlat: A laboratóriumi gyakorlat látogatása kötelező. Igazolatlan hiányzás esetén a laboratóriumi gyakorlat nem teljesített. A távolmaradás pótlásának nincs lehetősége. Igazolt hiányzás esetén a hallgató köteles a hiányzást követő első gyakorlaton bemutatni az igazolását az oktatónak. Félévközi ellenőrzések: hét(naptári) 1. (36) [2017-09-04]: 2. (37) [2017-09-11]: 3. (38) [2017-09-18]: 4. (39) [2017-09-25]: 1. mini zh 5. (40) [2017-10-02]: 2. mini zh 6. (41) [2017-10-09]: 3. mini zh 7. (42) [2017-10-16]: 4. mini zh 8. (43) [2017-10-23]: --/5. mini zh 9. (44) [2017-10-30]: 5. mini zh/-- 10. (45) [2017-11-06]: 6. mini zh 11. (46) [2017-11-13]: 7. mini zh 12. (47) [2017-11-20]: 8. mini zh 13. (48) [2017-11-27]: Nagy zh 14. (49) [2017-12-04]: Javító A 8. héten a hétfő elmarad (Október 23.), a kedd-csütörtöki órák rendben haladnak. A 9. héten hétfőn az előző hét lesz pótolva, a szerdai órák elmaradnak (November 1.). Kedden és csütörtökön gyakorló, konzultációs órák lesznek tartva, ezek látogatása nem kötelező, és a teremkapacitásig bármely órára be lehet menni A ZH-kon az azt megelőző hét végéig az előadáson vagy gyakorlaton elhangzott anyag kerül számonkérésre. A gyakorlat teljesítésének értékelése pontozás alapján történik. Maximálisan összegyűjthető pontszám 80 pont. Laboratóriumi gyakorlatok értékelése: 8 darab 20-45 perces mini ZH., egyenként 5-10, összesen 60 pontért. A mini ZH-k megírása a hálózatról lekapcsolt számítógépen, a kiértékelés a bíró rendszeren keresztül automatizáltan történik. 1 darab 135 perces nagy ZH., 20 pontért. A nagy ZH-n teljes programot kell megírni hálózatról lekapcsolt számítógépen. A feladatok értékelése a bíró rendszerén keresztül automatikus. Minimális pontszámok / javítás: Mini ZH-kon összesen minimum 15 pontot kell elérni, aki nem éri el az nem teljesítette a félévet. Mini ZH pótlására / javítására nincs lehetőség. A nagy ZH-n minimum 5 pontot kell elérni, a gyakorlaton összesen minimum 30 pont. Aki ezeket nem éri el, annak a nagy ZH pontszámát kiváltó javítót kell írnia. A többiek is írhatnak javítót, de a javító pontszáma akkor is felülírja a nagy zh pontszámát, ha rosszabbra sikerül (vagyis rontani is lehet)! A laboratóriumi gyakorlatokon való szereplésért (pl. házi feladatok táblánál történő bemutatásáért vagy meg nem oldásáért) plusz / mínusz pontok adhatók, amik az összpontszámba számítanak bele (így a gyakorlat 30 pontos minimumába is). A gyakorlat érdemjegyének meghatározása az összpontszám alapján történik a következő ponthatárok szerint: * 0 - 29 pont : elégtelen (1) * 30 - 44 pont : elégséges (2) * 45 - 59 pont : közepes (3) * 60 - 69 pont : jó (4) * 70 - 80 pont : jeles (5) Előadás: Az előadás látogatása nem kötelező, de a gyakorlat épít az előadáson elhangzottakra, így azt a hallgatóknak ismerniük kell! A vizsgára jelentkezés előfeltétele a gyakorlat sikeres teljesítése. A vizsgajelentkezés az ETR rendszeren keresztül történik. Vizsgáról való távollét igazolásánál a TVSZ szerint kell eljárni. A kollokvium érdemjegyének meghatározása a (várhatóan teszt jellegű) vizsgán szerzett pontok alapján történik a következő ponthatárok szerint: * 0 - 49% : elégtelen (1) * 50 - 64% : elégséges (2) * 65 - 79% : közepes (3) * 80 - 89% : jó (4) * 90 - 100% : jeles (5) A jó (4) és jeles (5) jegyekért szóbeli vizsgarészt is teljesíteni kell.
Linux operációs rendszer alapparancsai
1. gyakorlat - 09.04. - Évkezdési adminisztárció + Linux alapok
C programozási nyelv
2. gyakorlat - 09.11. - Fordítás & futtatás, változók ( lokális & globális ), input/output, blokkok, függvények
3. gyakorlat - 09.18. - Feltételes vezérlési szerkezetek (IF-ELSE, SWITCH) és ciklusok (WHILE, DO-WHILE, FOR)
4. gyakorlat - 09.25. - 1. kisZH (5p) - Konstansok (#define), 1D tömbök és karaktertömbök
5. gyakorlat - 10.02. - 2. kisZH (5p) - Tipusok haladó, Hibajavítás, Gyakorlás
6. gyakorlat - 10.09. - 3. kisZH (5p) - Rekurzió, Gyakorlás
7. gyakorlat - 10.16. - 4. kisZH (5p) - File input/output, Sorozatok, Gyakorlás (k-ad rendű Fibonacci, Beszúró rendezés)
8. gyakorlat - 10.23. - A GYAKORLAT ELMARAD! 1956-os Forradalom Ünnepe (hosszú hétvége).
9. gyakorlat - 10.30. - 5. kisZH (10p) - Pointerek
10. gyakorlat - 11.06. - 6. kisZH (10p) - typedef, struct, union; pointerek és struktúrák kombinálása
11. gyakorlat - 11.13. - 7. kisZH (10p) - FILE IO (ismétlés), Pointer haladó (2D tömbök), Láncolt lista, Függvény pointer
12. gyakorlat - 11.20. - 8. kisZH (10p) - enum, parancssori argumentumok, modulok, makrók, IF-ELSE kicsit másképp, tárolási osztályok
13. gyakorlat - 11.27. - NagyZH (20p)
14. gyakorlat - 12.04. - Javító/Pótló ZH
1. gyakorlat
Évkezdési adminisztráció
Ismerkedés
- Tűzvédelmi napló
- Szabályzatok
- Követelmények
Regisztráció (!)
- Regisztráció (http://www.stud.u-szeged.hu -> a bal menüből STUD regisztráció -> Adatlap kitöltése, majd küldése )
- Ekkor kapni fog mindenki egy aznonosítót (h-s azonosító: hxxxxxx - ahol az x-ek számok) és egy induló jelszót. Ezeket az adatokat írja fel mindenki mindenképp!
- Jelszóváltás - az előzőleg kapott jelszavat két helyen is meg kell változtatni:
- Jelszó leváltása STUD rendszeren: a bal menüből a Jelszóváltás kiválasztása.
- Jelszó leváltása a kabinethez: https://www.inf.u-szeged.hu/jelszo, jelszóváltó űrlap segítségével.
- Bejelentkezés - két külön rendszerbe tudsz bejelentkezni ezután:
- A STUD szerverre (levelezés, honlapkészítés)
- A kabinetbe (tantermi gépek, saját mappa, bíró szerver, stb.).
Levelezés (!)
- Ezután már levelezni is tudsz a STUD-os email címeddel.
- Jelentkezz be a levelező felületre a STUD szerver bal oldali menüjében lévő Levelezés / Horde / menüpont megnyitásával és az adataid megadásával.
- Azonosítód (itt és a kabinetes bejelentkezéskor is) a h-s azonosítód, a jelszavad pedig a módosított, általad az előbbiekben megadott jelszó.
- A te email címed formája:
- hxxxxxx@stud.u-szeged.hu (pl.: h123456@stud.u-szeged.hu) VAGY
- <Vezetéknév>.<Keresztnév, vagy Keresztnevek ponttal elválasztva>[sorszám]@stud.u-szeged.hu (pl.: Nagy.Bela.Janos.2@stud.u-szeged.hu)
- Majd sikeres bejelentkezés után mindenki küldjön nekem egy emailt a leko@inf.u-szeged.hu címre, melynek a szerkezete az alábbi módon nézzen ki:
- Tárgy (Copy-Paste használata ajánlott):
- Hétfői 8-11 csoport esetén: [progalap2017][8][reg]
- Hétfői 15-18 csoport esetén: [progalap2017][15][reg]
- Tartalom:
- h-s azonosító;Neptun-kód;név
Egyéb (PUB és HOME)
-
A kabinetes rendszerben mindenki kap egy mappát ("home mappa"), amiben az órai munkáit tárolhatja. Ezen a kvóta 300 MB illetve 10000 fájl.
Ezenkívül mindenki eléri a pub-könyvtárat, melyben a tananyagok találhatók.
Otthonról való bejelentkezéshez és adatmozgatáshoz az alábbi programokat ajánlom (Windows alatt):
- WinSCP - Adatmozgatás a kabinetes szerverről/szerverre otthonról könnyedén.
- PUTTY - Otthonról való bejelentkezés a kabinetes szerverre.
Kabinet elereséhez részletes leírás itt .
- Próbáljuk is ki a saját HOME-unk felcsatolását, melyet a "Mount Home" iconra kattintás után a h-s azonosítónk és jelszavunk megadásával tehetünk meg. Tesztként hozzunk létre pl. egy üres dokumentumot, mentsük le, majd csatoljuk le a HOME-unkat - "Unmount Home". Egy ismételt felcsatlakozással ellenőrizzük le, hogy az előzőekben létrehozott file továbbra is megtalálható-e ott, ahol elhelyeztük. Minden felcsatolás esetén, használat után csatlakoztassuk le a HOME-unkat!
Linux alapok
Debian (az ez alatti linkeken belül szerepel, hogy melyiket ajánlott letölteni), Ubuntu és VirtualBox letöltési lehetőség.
Debian telepítés leírása (általános) itt .
Debian telepítés leírása (64 bit!) itt . (EZT AJÁNLOM. Részletes, érthető és jól működik - tesztelve)
A weboldalon fellelhető LINUX anyag fő forrásai: itt és itt .
Fájlkezelés
pwd
(print working directory)
Aktuális könyvtár elérési útvonala. Bármikor kiadhatod ezt a parancsot, eredményként megkapod, hogy pontosan "hol" is vagy.
lekogabi@lekogabi:~/Letöltések/eclipse$ pwd /home/lekogabi/Letöltések/eclipse
history
Kiírja a terminal előzményeket.
ls
(list)
Ez a parancs megmutatja az aktuális könyvtárban tálalható fájlok neveit. A lista alapértelmezés szerint abc sorrendben sorolja fel a neveket, de alkalmas kapcsolók segítségével más sorrendet is elõírhatunk. pl.:
lekogabi@lekogabi:~/Letöltések/eclipse$ ls
Eredményként kiíratódik a terminálban az 'eclipse' mappa tartalma.
Kapcsolók:
-l: oszlopokban, részletesen mutatja a fájlok adatait
-d <könyvtár>: csak az adott könyvtár adatait írja ki. (Használjuk a "-l" kapcsolót is.)
-a: a rejtett fájlokat is mutatja
-R: könyvtáron belüli könyvtárak tartalmát is listázza
-r: forditott sorrendben listáz
-h: "barátságosabb" formában listázza ki a számokat
A kapcsolókat lehet kombinálva is használni, igy pl.:
lekogabi@lekogabi:~/Letöltések/eclipse$ ls -l -a -r lekogabi@lekogabi:~/Letöltések/eclipse$ ls -lar
Mindkét parancs kifogja listázni oszlopokba rendezve (-l), részletes adatokkal a rejtett fájlokat (-a) is, forditott sorrendben (-r).
cd <none, könyvtár, könyvtárszerkezet>
(change directory)
A könyvtár rendszerben való mozgást teszi lehetõvé. Paraméterként a megcélzott könyvtár nevét kell megadni, vagy abszolút, vagy relatív elérési útvonal használatával.
Abszolút elérési útvonal megadásánál a keresett könyvtár teljes elérési útvonalát kell megadni. Tehát rootból indulva pl.: /home/hxxxxxx/mappa/file1 .
Relatív címzés esetén azt mondjuk csak meg, hogy az aktuális könyvtárhoz képest hol helyezkedik el a megcélzott könyvtár. Tehát aktuális könyvtárból indulva pl.: ha éppen a /home/hxxxxxx-ben vagyunk: ./mappa/file1 .
aktuális könyvtár: .
szülőkönyvtár: ..
További példák:
Abszolút címzés:
lekogabi@lekogabi:~$ lekogabi@lekogabi:~$ cd /home/lekogabi/Letöltések/eclipse lekogabi@lekogabi:~/Letöltések/eclipse$
Relatív címzés:
lekogabi@lekogabi:~$ lekogabi@lekogabi:~$ cd Letöltések/eclipse/ lekogabi@lekogabi:~/Letöltések/eclipse$
Visszalépés a szülő könyvtárba:
lekogabi@lekogabi:~/Letöltések/eclipse$ cd .. lekogabi@lekogabi:~/Letöltések$ cd .. lekogabi@lekogabi:~$vagy
lekogabi@lekogabi:~/Letöltések/eclipse$ cd ../.. lekogabi@lekogabi:~$
A cd parnacs önmagában visszadob a hxxxxxx könyvtárba.
lekogabi@lekogabi:~/Letöltések/eclipse$ cd lekogabi@lekogabi:~$
"~": a home könyvtár elérési útvonalát tárolja.
lekogabi@lekogabi:~/Letöltések/eclipse$ cd ~ lekogabi@lekogabi:~$
mkdir [kapcsoló]<új mappa>
(make directory)
Új könyvtár létrehozására (csak az adott könyvtárban). pl.:
lekogabi@lekogabi:~/Dokumentumok$ mkdir hello
Létrejön egy "hello" nevű mappa a 'Dokumentumok'-on belül.
Kapcsolók:
-p: Létrehozza a kívánt könyvtár eléréséig az összes szükséges könyvtárat. pl.:
lekogabi@lekogabi:~/Dokumentumok$ mkdir -p hello/hello2/hello3
Végig létrehozza a könyvtárszerkezetet.
-m: Megadhatjuk az új katalógus hozzáférési jogát oktálisan a "mode" értékének beállításával. pl.:
lekogabi@lekogabi:~/Dokumentumok/hello$ mkdir -m 777 hello2 lekogabi@lekogabi:~/Dokumentumok/hello$ ls -l drwxrwxrwx 2 lekogabi lekogabi 4096 aug 5 17:05 hello2
A '777' megadásával teljeskörű jogokat biztosítunk mindhárom felhasználói csoportnak (user, group, other). A 3 db 7-es számjegy egyenkénti csökkentésével (0-7) lehet egyre kisebb jogokkal felruházni a felhasználói csoportokat.
Az elérési jogok megváltoztatásáról/értelmezéséről részletesen a "Fájlkezelés" alfejezet végén (chmod).
rmdir [kapcsoló]<törlendő mappa>
(remove directory)
Könyvtárak törlésére szolgáló parancs. Az rmdir parancs csak üres könyvtárat vagy könyvtárakat töröl. pl.:
lekogabi@lekogabi:~/Dokumentumok$ mkdir hello lekogabi@lekogabi:~/Dokumentumok$ rmdir hello
Eredményül kitörlődik a 'hello' nevű mappa.
Kapcsolók:
-p: A könyvtár törlése után, a szülõ könyvtárat is törli, ha üres, rekurzívan.
A Dokumentumok mappán belül található egy hello nevű mappa és azon belül egy hello2 nevű mappa. Ha az aktuális könyvtár a Dokumentumok, akkor a -p kapcsoló segítségével, ha kitöröljük a hello2 mappát, akkor a hello mappa is törlődni fog (mivel nem tartalmaz mást).
lekogabi@lekogabi:~/Dokumentumok$ mkdir -p hello/hello2 lekogabi@lekogabi:~/Dokumentumok$ rmdir -p hello/hello2
rm [kapcsoló]<törlendő fájl(ok)>
(remove)
Kitörli az rm parancs után található fájlokat (könyvtárakat nem, arra az rmdir használandó, kivéve kapcsoló hozzáadásával - lásd lentebb). pl.:
lekogabi@lekogabi:~/Dokumentumok$ rm file1.txt file2.txt
Eredményül a file1.txt és a file2.txt törlésre kerül.
Kapcsolók:
-f: sosem kérdez.
-i: kétes esetben kérdez.
-r: könyvtár törlésére, akkor is törli ha nem üres!
-R: -//-
lekogabi@lekogabi:~/Dokumentumok$ ls elso lekogabi@lekogabi:~/Dokumentumok$ cd elso/masodik lekogabi@lekogabi:~/Dokumentumok/elso/masodik$ ls t.txt lekogabi@lekogabi:~/Dokumentumok/elso/masodik$ cd ../.. lekogabi@lekogabi:~/Dokumentumok$ rm -r elso lekogabi@lekogabi:~/Dokumentumok$ ls lekogabi@lekogabi:~/Dokumentumok$
Kitörlésre kerül az elso/masodik mapparendszer, annak ellenére, hogy nem üres.
mv [kapcsoló]<forrás><cél>
(move)
A fájlok állományrendszeren belüli mozgatására szolgáló parancs. Ha nem adunk meg fájlnevet a célnál, akkor nem változik meg a neve, különben a megadott fájlnév lesz a célkönyvtárban. pl.:
lekogabi@lekogabi:~/Dokumentumok$ mv hello /home/lekogabi/Letöltések
Eredményül a 'hello' nevű fájl átkerül a Dokumentumok könyvtárból a Letöltések könyvtárba.
Kapcsolók:
-b: biztonsági másolatot készít a forrásfájlról.
-f: sosem kérdez felülírásnál.
-i: kétes esetben kérdez (pl. névütközés...).
-u: csak régebbit ír felül.
cp [kapcsoló]<forrás><cél>
(copy)
Átmásolja a forrás fájlt a megadott helyre. pl.:
lekogabi@lekogabi:~/Letöltések$ cp image.png /home/lekogabi/Dokumentumok
Eredményül átmásolja a 'image.png' fájlt a Letöltések könyvtárból a Dokumentumok könyvtárba.
Kapcsolók:
-r: könyvtár egész tartalmának másolása.
-R: -//-
-b: minden célfájlról mentés.
-f: sosem kérdez.
-i: kétes esetben kérdez.
-u: csak régebbit írja felül.
-l: linkelés másolás helyett.
-s: szimbólikus linket készít.
ln [kapcsoló]<forrás><cél>
(link)
Az első paraméter a fájl, amihez szeretnénk linket készíteni, a második paraméter a link neve. Kapcsoló nélkül hard link készítése.
Kapcsolók:
-s: Szimbólikus (soft) link készitése.
Hard link pl.:
lekogabi@lekogabi:~/Dokumentumok$ cat > file1.txt hello lekogabi@lekogabi:~/Dokumentumok$ ln file1.txt file2.txt lekogabi@lekogabi:~/Dokumentumok$ ls -l -rw-r--r-- 2 lekogabi lekogabi 6 aug 7 01:15 file1.txt -rw-r--r-- 2 lekogabi lekogabi 6 aug 7 01:15 file2.txt lekogabi@lekogabi:~/Dokumentumok$ cat >> file1.txt world!
Létrehozunk egy .txt fájlt 'file1' névvel, melybe a "hello" szöveget irtuk. Majd készitettünk egy hard linket a fájlunkról 'file2' névvel. Majd módositottuk a file1-et, amelynek eredményeképp a file2 is módosult. Így mindkét .txt fájlban a "hello world!" szöveg fog szerepelni. Listázásnál a második mezõ jelenti a fájlra mutató hard linkek számát. Ha kitöröljük az egyik fájlt, attól a másik még használható. Gondolhatunk a Hard link-re egyfajta biztonsági mentés + automatikus módosítás kombinációra.
Szimbólikus link:
lekogabi@lekogabi:~/Dokumentumok$ ln -s file1.txt file3.txt lekogabi@lekogabi:~/Dokumentumok$ ls -l -rw-r--r-- 2 lekogabi lekogabi 13 aug 7 01:16 file1.txt -rw-r--r-- 2 lekogabi lekogabi 13 aug 7 01:16 file2.txt lrwxrwxrwx 1 lekogabi lekogabi 9 aug 7 01:29 file3.txt -> file1.txt lekogabi@lekogabi:~/Dokumentumok$ cat >> file1.txt lekogabi@lekogabi:~/Dokumentumok$ rm file1.txt lekogabi@lekogabi:~/Dokumentumok$ ls -l -rw-r--r-- 1 lekogabi lekogabi 29 aug 7 01:34 file2.txt lrwxrwxrwx 1 lekogabi lekogabi 9 aug 7 01:29 file3.txt -> file1.txt
A meglvévő file1.txt fájlunkhoz készítünk egy szimbólikus linket. Listázás után ez jól megfigyelhető az elkészült file3.txt után látható nyil alapján, amely a file1.txt-re mutat. A hard linkkel ellentétben, amennyiben töröljük a file1.txt-t, akkor a file3.txt használhatatlan lesz, mivel töröltük a fájlt, amire mutatott.
du
A fájljaink és könyvtáraink által elfoglalt lemezterületet lehet vele megtekinteni. pl.:
lekogabi@lekogabi:~/Dokumentumok$ du file2.txt 4 file2.txt lekogabi@lekogabi:~/Dokumentumok$ du file2.txt hello 4 file2.txt 4 hello
Kapcsolók:
-a: minden fájl adatait kiírja a könyvtár(ak)on belül, nem csak a könyvtár(ak)ét.
-h: "barátibb" alakban a számok.
-m: megabájtban írja ki a méretet.
-s: csak összméret.
chmod
Az elérési jogok megváltoztatása a chmod paranccsal lehetséges a tulajdonos számára. Szintaxisa pedig:
1. verzió:
chmod [ugoa][+-][rwx] fájlnév
Az [ugoa] kapcsolókkal írjuk elõ, hogy kinek adjuk a jogot. Adhatunk jogokat a tulajdonosnak (u - user), a csoportnak (g - group), másoknak (o - others) vagy pedig mindenkinek egyszerre (a - all). A [+-] azt jelenti, hogy adhatunk (+) vagy elvehetünk (-) jogokat. Végül pedig, hogy olvasási (r - Read), írási (w - Write) vagy futtatási (eXecute) jogot adunk. pl.:
lekogabi@lekogabi:~/Dokumentumok$ ls -l drwxrwxrwx 2 lekogabi lekogabi 4096 aug 9 15:07 hello lekogabi@lekogabi:~/Dokumentumok$ chmod g-w hello lekogabi@lekogabi:~/Dokumentumok$ ls -l drwxr-xrwx 2 lekogabi lekogabi 4096 aug 9 15:07 hello
2. verzió
chmod <xxx> fájlnév
Hasonló az előzőhöz, viszont itt minden x helyén 0-7-ig való osztályozással adható meg a különböző felhasználói csoportok jogosultágai.
Rendre egy darab x helyettesítésének jelentése:
0: 000 --- semmi jog 1: 001 --x csak futtatási 2: 010 -w- csak írási 3: 011 -wx írási és futtatási 4: 100 r-- csak olvasási 5: 101 r-x olvasási és futtatási 6: 110 rw- olvasási és írási 7: 111 rwx olvasási, írási és futtatási jog
pl.:
lekogabi@lekogabi:~/Dokumentumok$ ls -l drwx----wx 2 lekogabi lekogabi 4096 aug 9 15:07 hello lekogabi@lekogabi:~/Dokumentumok$ chmod 777 hello lekogabi@lekogabi:~/Dokumentumok$ ls -l drwxrwxrwx 2 lekogabi lekogabi 4096 aug 9 15:07 hello
Kapcsolók:
-R: rekurzívan az összes fájlra és alkönyvtárra.
zip / unzip
Adott fájl vagy mappa be- és kicsomagolása.
Fájl esetén
lekogabi@lekogabi:~/Dokumentumok$ zip file.zip file2.txt adding: file2.txt (stored 0%) lekogabi@lekogabi:~/Dokumentumok$ unzip file.zip Archive: file.zip replace file2.txt? [y]es, [n]o, [A]ll, [N]one, [r]ename: y extracting: file2.txt
Mappa esetén (rekurzívan)
lekogabi@lekogabi:~/Dokumentumok$ zip -r hello.zip hello updating: hello/ (stored 0%) updating: hello/file.txt (stored 0%) lekogabi@lekogabi:~/Dokumentumok$ rm -r uj lekogabi@lekogabi:~/Dokumentumok$ unzip hello.zip Archive: hello.zip creating: hello/ extracting: hello/file.txt lekogabi@lekogabi:~/Dokumentumok$ ls file2.txt hello hello.zip lekogabi@lekogabi:~/Dokumentumok$
Kapcsolók:
-r: rekurzívan az összes fájlra és alkönyvtárra.
Felhasználók
finger <argumentum>
who
w
Kiírja, hogy kik vannak bejelentkezve az aktuális gépre.
felhasználó: a megadott felhasználóról ír ki adatokat.
Kiírja, hogy kik vannak bejelentkezve az aktuális gépre, plusz adatokat ír ki a finger-hez képest.
Ugyanaz, mint a who, csak kiírja, hogy min dolgozik.
Multitask
ps
jobs
^C
^Z
bg %<szám>
fg %<szám>
kill %<szám>/PID
Kiírja a képernyőre az aktuális process-eket (futó vagy várakozó programokat, amik az adott terminálhoz kapcsolódnak).
-e: az összes futó process-t kiírja (más terminálhoz/terminálhoz nem kacspoltakat is).
-f: több információ.
-u user: csak a megadott user által "birtokolt" processzek listázása.
Kiírja az aktuális jobokat, amik az adott terminálhoz kapcsolódnak.
Ctrl+c paranccsal leállíthatunk előtérben futó folyamatokat.
Ctrl+z paranccsal szüneteltethetünk előtérben futó folyamatokat.
Várakozó job-ok elinditását teszi lehetővé, a "szám" helyére a job azonosítója kerül, háttérben indítja el (konzolt nem "használja").
Várakozó job-ok elindítását teszi lehetővé, a "szám" helyére a job azonosítója kerül, előtérben indítja el (konzolt "használja").
Process-eket és job-okat tudunk vele leállítani.
%<szám> formátumnál a szám helyére a job azonosítóját kell írni.
PID a process azonosítójának felel meg, és ezt a process-t szakítja meg (csak akkor, ha fut).
Kapcsolók:
-9: a leállított process-t is megszakítja.
-s: úgy állítja le a job-ot, hogy még újra lehet indítani.
Példa a job-ok kezelésére:
lekogabi@lekogabi:~$ cat > file.txt hello ^C lekogabi@lekogabi:~$ cat > file.txt hello ^Z [1]+ Megállítva cat > file.txt lekogabi@lekogabi:~$ jobs [1]+ Megállítva cat > file.txt lekogabi@lekogabi:~$ fg %1 cat > file.txt world ^Z [1]+ Megállítva cat > file.txt lekogabi@lekogabi:~$ jobs [1]+ Megállítva cat > file.txt lekogabi@lekogabi:~$ kill %1 [1]+ Megállítva cat > file.txt lekogabi@lekogabi:~$ jobs [1]+ Befejeződött cat > file.txt lekogabi@lekogabi:~$ jobs lekogabi@lekogabi:~$
Beleírtunk egy sort a file.txt-be, majd leállítottuk (Ctrl+c) a folyamatot. Aztán ismét írtunk bele egy sort, viszont ekkor csak szüneteltettük (Ctrl+z) a folyamatot. jobs paranccsal lekértük az aktuális job-okat, ahol látható az előbbi fájlba írás folyamatunk. fg paranccsal "vissza hívtuk" a job-ot. Ismét írtunk bele egy sort, majd várakozni küldük és a következő lépésben a kill paranccsal "kilőttük" a folyamatot. Ezután a jobs paranccsal lekérdezve már látható, hogy a fájlba írás teljes egészében leállt.
&
Ha egy progamot a háttérben akarunk futtatni, akkor a futtatandó program neve után egy & jelet kell tenni. pl.:
lekogabi@lekogabi:~$ gedit&
A fenti parancs hatására megnyílik a szövegszerkesztő a háttérben.
Szövegfájlok
echo
Az utána lévő szöveget írja ki a képernyőre. A szóközöket a "\<szóköz>" segítségével lehet beszúrni. Vagy ha az egész szöveget "" közé tesszük, akkor az kerül a képernyőre ami az "" között van. pl.:
lekogabi@lekogabi:~$ echo hello hello lekogabi@lekogabi:~$ echo "hello Kitty!" hello Kitty!
Változók is előállíthatóak az echo segítségével egy plussz '$' jel használatával. pl.:
lekogabi@lekogabi:~$ echo $asd (semmi) lekogabi@lekogabi:~$ asd="hello" lekogabi@lekogabi:~$ echo $asd hello lekogabi@lekogabi:~$ unset asd lekogabi@lekogabi:~$ echo $asd (semmi)
Létrehoztunk egy 'asd' nevű változót, amely kezdetben üres. Majd bele tettük a "hello" szöveget, majd az 'unset' parancs segítségével töröltük.
set
Kiírja az összes környezeti változót és az értéküket.
cat <fájl>
Paraméter nélkül írhatunk a képernyőre és Enter leütése után kiírja az addig beírt sort. Fájl paraméter esetén kiírja a fájl tartalmát a képernyőre. Ha "cat > szövegfájl" kombinációt használjuk, akkor a konzolra írt sorokat a szövegfájlba menti, ha >>-t írunk > helyett, akkor pedig a sorokat hozzáfűzi a fájl végéhez. pl.:
lekogabi@lekogabi:~/Dokumentumok$ cat hello hello ^C lekogabi@lekogabi:~/Dokumentumok$ cat > file.txt first ^C lekogabi@lekogabi:~/Dokumentumok$ cat file.txt first lekogabi@lekogabi:~/Dokumentumok$ cat >> file.txt second ^C lekogabi@lekogabi:~/Dokumentumok$ cat file.txt first second
Először kiírtuk a képernyőre, hogy "hello", majd a 'file.txt'-be bele írtuk a "first" sort. Aztán ezt kiírattuk a képernyőre, majd hozzáfűztük ugyanehhez a fájlhoz a "second" sort és kiírattuk.
Cat és a job műveletek használata, pl.:
lekogabi@lekogabi:~$ cat hello hello ^Z [1]+ Megállítva cat lekogabi@lekogabi:~$ jobs [1]+ Megállítva cat lekogabi@lekogabi:~$ fg %1 cat hi hi ^Z [1]+ Megállítva cat lekogabi@lekogabi:~$ jobs [1]+ Megállítva cat lekogabi@lekogabi:~$ kill %1 [1]+ Megállítva cat lekogabi@lekogabi:~$ jobs [1]+ Befejeződött cat lekogabi@lekogabi:~$ jobs lekogabi@lekogabi:~$
more
A fájl tartalmát oldalanként írja ki a képernyőre. ('Space' leütésével lehet léptetni.)
head [kapcsoló]<fájl>
Kiírja a fájl a bizonyos sorait a képernyőre.
Kapcsolók:
-n <-szám>: kiírja a file tartalmát, kivéve az utolsó "szám" mennyiségű sort.
-szám: az első "szám" számú sort írja ki
tail [kapcsoló]<fájl>
Kiírja a fájl a bizonyos sorait a képernyőre.
-n <+szám>: a "szám"-adik sortól kezdve ír ki.
-szám: az utólsó "szám" számú sort írja ki
-f: folyamatosan bővülő fájl tartalmát írja ki.
head, tail és head|tail pl.:
lekogabi@lekogabi:~/Dokumentumok$ cat file2.txt elso masodik harmadik negyedik otodik hatodik hetedik nyolcadik kilencedik tizedik lekogabi@lekogabi:~/Dokumentumok$ head -4 file2.txt elso masodik harmadik negyedik lekogabi@lekogabi:~/Dokumentumok$ tail -2 file2.txt kilencedik tizedik lekogabi@lekogabi:~/Dokumentumok$ head -9 file2.txt | tail -3 hetedik nyolcadik kilencedik
wc [kapcsoló]<fájl>
Kiírja a fájl sorainak a számát, szavainak a számát, a byte-ok számát és a fájl nevét.
Kapcsolók:
-l: csak a nevet és a sorok számát írja ki.
-w: csak a nevet és a szavak számát írja ki.
-c: csak a nevet és a byte-ok számát írja ki.
-m: csak a nevet és a betűk számát írja ki.
pl.:
lekogabi@lekogabi:~/Dokumentumok$ wc file2.txt 10 10 83 file2.txt lekogabi@lekogabi:~/Dokumentumok$ wc -l file2.txt 10 file2.txt
^D
Ctrl+d parancs megszünteti a bemenetet vagy kilép a terminálból (vagy shell programból)
/dev/null
Egy speciális fájl, amely minden beleírt adatot töröl, miközben az írasi művelet sikeres. Az eszközből való olvasás nem ad vissza semmilyen adatot, eredménye azonnali EOF, azaz fájl vége. Felfogható adatnyelő eszközként ("fekete lyuk"). A null eszközt tipikusan folyamatok kimeneti stream-jeinek eltüntetésére használják, vagy az üres bemenet biztosítására, általában átirányítás segítségével. pl.: OpenGL: ./LekoGabor > /dev/null
Beépített környezeti változók:
$PWD: Tárolja az aktuális elérési útvonalat.
$HOME: Tárolja a home könyvtár abszolút elérési útvonalát.
$PS1: A prompt kinézetét leíró kifejezést tárolja.
$PATH: A keresési könyvtárak elérési útvonalát tárolja, itt keresi a parancsokat.
~: Nem környezeti változó, de a home könyvtár elérési útvonalát tárolja.
Mintaillesztés
Ha nem tudjuk egy szöveg pontos alakját, csak egy részét, vagy több szövegrészlettel szeretnénk egyszerre dolgozni, akkor jön jól a mintaillesztés. Használható pl. egyszerre több fájl törlésénél, vagy keresésnél (fájlban, vagy fájlrendszerben).
Speciális karakterek:
? - pontosan egy karaktert helyettesít: pl. ?lma lehet alma vagy álma is így.
* - bármennyi (akár 0) karaktert helyettesít: pl. *gép lehet gép, mosógép, számítógép, stb.
[...] - a [] között felsorolt karakterekből pontosan egyet helyettesít: pl. [aó]lom lehet ólom vagy alom, fajl[0123456789] pedig fajl0, fajl1, ... fajl9.
pl.:
Az összes .txt végződésű fájl kilistázása:
lekogabi@lekogabi:~$ ls *.txt
Minden .png fájl átmásolása a Dokumentumok/hello mappába:
lekogabi@lekogabi:~$ cp *.png /home/lekogabi/Dokumentumok/hello
Az összes .png kép törlése a Dokumentumok/hello mappából:
lekogabi@lekogabi:~/Dokumentumok/hello$ rm *.png
grep [kapcsoló][minta]<fájl>
Kiírja egy fájl azon sorait, amelyekben szerepel a minta szövegrészlet.
Kapocsolók:
-A # : # db sor kiírása az illeszkedő sorok után.
-B # : # db sor kiírása az illeszkedő sorok előtt.
-C # : # db sor kiírása az illeszkedő sorok előtt és után.
-e minta : minta megadása; segítségével több minta is megadható, illetve akkor is jó, ha a minta a - karakterrel kezdődik.
-r, -R: könyvtárak feldolgozása rekurzívan.
-v: azon sorait írja ki, amik nem tartalmazzák a minta szövegrészletet.
pl.:
lekogabi@lekogabi:~/Dokumentumok$ grep hat file2.txt hatodik lekogabi@lekogabi:~/Dokumentumok$ grep ed file2.txt negyedik hetedik kilencedik tizedik lekogabi@lekogabi:~/Dokumentumok$ grep -v ed file2.txt elso masodik harmadik otodik hatodik nyolcadik lekogabi@lekogabi:~/Dokumentumok$
Átirányítások és parancsok láncolása
parancs > szövegfájl
A parancs kimenete a konzol helyett a fájlba fog íródni, a fájl addigi tartalma elvész. pl.:
lekogabi@lekogabi:~/Dokumentumok$ ls -l > file.txt lekogabi@lekogabi:~/Dokumentumok$ cat file.txt -rw-r--r-- 1 lekogabi lekogabi 83 aug 9 21:01 file2.txt -rw-r--r-- 1 lekogabi lekogabi 0 aug 13 19:10 file.txt drwxr-xrwx 2 lekogabi lekogabi 4096 aug 9 15:07 hello -rw-r--r-- 1 lekogabi lekogabi 15796 aug 13 19:09 progalap_linux
parancs >> szövegfájl
A parancs kimenete a konzol helyett a szövegfájl végéhez fog hozzáfűződni. pl.:
lekogabi@lekogabi:~/Dokumentumok$ ls >> file.txt lekogabi@lekogabi:~/Dokumentumok$ cat file.txt -rw-r--r-- 1 lekogabi lekogabi 83 aug 9 21:01 file2.txt -rw-r--r-- 1 lekogabi lekogabi 0 aug 13 19:10 file.txt drwxr-xrwx 2 lekogabi lekogabi 4096 aug 9 15:07 hello -rw-r--r-- 1 lekogabi lekogabi 15796 aug 13 19:09 progalap_linux file2.txt file.txt hello progalap_linux
parancs < szövegfájl
A parancs bemeneteként kapja a szövegfájl tartalmát.
parancs1 | parancs2
A parancs1 kimenete konzolra kiírás helyett a parancs2 bemenete lesz. pl.:
lekogabi@lekogabi:~/Dokumentumok$ cat file2.txt elso masodik harmadik negyedik otodik hatodik hetedik nyolcadik kilencedik tizedik lekogabi@lekogabi:~/Dokumentumok$ head -9 file2.txt | tail -3 hetedik nyolcadik kilencedik
parancs1 && parancs2
A parancs1 végrehajtása után a parancs2 csak akkor hajtódik végre, ha a parancs1 hiba nélkül futott le. pl.:
lekogabi@lekogabi:~/Dokumentumok$ mkdir ujmappa && rmdir ujmappa
Ha létre tudta hozni az 'ujmappa' nevű könyvtárat, akkor ki is törli a második paranccsal.
parancs1 || parancs2
A parancs1 végrehajtása után a parancs2 csak akkor hajtódik végre, ha a parancs1 futása közben hiba történt. pl.:
lekogabi@lekogabi:~/Dokumentumok$ rm -r ujmappa || mkdir ujmappa
Megpróbáljuk törölni az 'ujmappa' nevű könyvtárat. Mondjuk még nincs ilyen könyvtár, akkor hiba üzenetet kapunk, majd létrejön az 'ujmappa' nevű könyvtár.
parancs1;parancs2;parancs3
A parancsok ";"-vel elválasztva egymás után hajtódnak végre balról jobbra.
lekogabi@lekogabi:~/Dokumentumok$ mkdir ujmappa; cd ujmappa; ls -l összesen 0 lekogabi@lekogabi:~/Dokumentumok/ujmappa$
Linux hálózatok
scp <felhasználónév>@<szerver>:<távoli útvonal><helyi útvonal>
Átmásol egy bizonyos fájlt vagy könyvtárat a "távoli útvonal"-ról a "helyi útvonal"-ra, vagy vissza. pl.:
lekogabi@lekogabi:~$ scp h165057@linux.inf.u-szeged.hu:/n/pub/ProgramozasAlapjai/Gyakorlat/gyak02/anyag.txt ./ h165057@linux.inf.u-szeged.hu's password: anyag.txt 100% 3659 3.6KB/s 00:01
ssh [kapcsoló]<felhasználónév>@<szerver>
Csatlakozni lehet a szerverre, futtatni konzolos programokat.
-X: Ezzel a kapcsolóval grafikus programot is indíthatunk.
pl.:
lekogabi@lekogabi:~$ ssh h165057@linux.inf.u-szeged.hu h165057@linux.inf.u-szeged.hu's password: Linux linux1.inf.u-szeged.hu 3.2.0-4-686-pae #1 SMP Debian 3.2.54-2 i686 ****************************************************************************** * Udvozoljuk az Informatikai Tanszekcsoport linux szerveren! * * * * A szerver azt a celt szolgalja, hogy tavoli bejelentkezes eseten ugyanazt * * a kornyezetet biztositsa, amit egy tanteremben levo munkaallomas biztosit. * * * * A szerver mukodesevel, a bejelentkezessel kapcsolatos problemaikat kerjuk * * a kabinet@inf.u-szeged.hu cimen jelezzek. * ****************************************************************************** Last login: Sun May 4 21:38:49 2014 from 79.101.5.52 h165057@linux1:~$ cd LekoGabor h165057@linux1:~/LekoGabor$ make make: Nothing to be done for `application'. h165057@linux1:~/LekoGabor$ ./LekoGabor freeglut (./LekoGabor): failed to open display '' h165057@linux1:~/LekoGabor$ exit kijelentkezés Connection to linux.inf.u-szeged.hu closed. lekogabi@lekogabi:~$ ssh -X h165057@linux.inf.u-szeged.hu h165057@linux.inf.u-szeged.hu's password: Linux linux1.inf.u-szeged.hu 3.2.0-4-686-pae #1 SMP Debian 3.2.54-2 i686 ****************************************************************************** * Udvozoljuk az Informatikai Tanszekcsoport linux szerveren! * * * * A szerver azt a celt szolgalja, hogy tavoli bejelentkezes eseten ugyanazt * * a kornyezetet biztositsa, amit egy tanteremben levo munkaallomas biztosit. * * * * A szerver mukodesevel, a bejelentkezessel kapcsolatos problemaikat kerjuk * * a kabinet@inf.u-szeged.hu cimen jelezzek. * ****************************************************************************** Last login: Wed Aug 13 21:12:17 2014 from 79.101.5.234 h165057@linux1:~$ cd LekoGabor/ h165057@linux1:~/LekoGabor$ make make: Nothing to be done for `application'. h165057@linux1:~/LekoGabor$ ./LekoGabor > /dev/null
sftp <felhasználónév>@<szerver>
Csatlakozik a szerverre és lehetőségünk van lépkedni a szerver könyvtáraiban, illetve a lokális könyvtárunkban, majd leszedni illetve feltölteni bizonyos adatokat.
Egy 'l' betű hozzáadásával tudjuk közölni a géppel, hogy nem a szerveren szeretnék, hogy végrehajtódjon az adott parancs, hanem a lokális gépen. pl.:
ls - lls, pwd - lpwd, cd - lcd, mkdir - lmkdir.
put - fájl átmásolása a lokális gépről a szerver gépre.
get - fájl átmásolása a szerver gépről a lokális gépre.
exit - kilép a szerverről.
pl.:
lekogabi@lekogabi:~$ sftp h165057@linux.inf.u-szeged.hu h165057@linux.inf.u-szeged.hu's password: Connected to linux.inf.u-szeged.hu. sftp> cd ../.. sftp> cd pub/ProgramozasAlapjai/Gyakorlat/gyak02/ sftp> get anyag.txt Fetching /n/pub/ProgramozasAlapjai/Gyakorlat/gyak02/anyag.txt to anyag.txt /n/pub/ProgramozasAlapjai/Gyakorlat/gyak02/an 100% 306 0.3KB/s 00:00 sftp> exit lekogabi@lekogabi:~$
2. gyakorlat
C alapok
GCC Windows-ra: Code::Blocks , Cygwin
Első lépések
Kommentek:
// Ez egy egysoros komment, ami itt van az nem fog látszani a programban
/*
Ez egy többsoros komment, ez sem fog látszani a programban
*/
Main - fő függvény
/*
MAIN függvény, ez a függvény fog lefutni először
*/
int main(){
return 0; // a main függvény visszatérési értéke: Op. rendszer felé: Nincs hiba
/* return 1; // a main függvény visszatérési értéke: Op. rendszer felé: Valami hiba történt */
}
Input - Output használata
/*
A ki- és bevitel használatához szükségünk van az alábbi sorra.
*/
#include <stdio.h>
/*
Ezek után használhatjuk a scanf és printf I/O függvényeket
Az input-output használatához meg kell mondani a fordítónak, hogy szeretnénk használni az ezzel kapcsolatos függvényeket.
*/
Fordítás és futtatás
Fordítás és futtatás Linux alatt (terminalban): $ gcc -o prog progalap.c // fordítás $ ./prog // futtatás Object fájl készítése: $ gcc -c -o progalap.o progalap.c $ gcc -o progalap progalap.o Warning-ok kiíratása fordítás után: $ gcc -Wall -o hello helloworld.c
Szekvenciák
Minden utasítást pontosvessző (";") zár.
Ha nem teszünk pontos vesszőt, akkor kifejezésről beszélünk.
Kifejezés - egy (önmagában még nem végrehajtható) elemi művelet. pl.: 3 + 5
Utasítások sorozatát szekvenciának nevezzük.
Példa:
main() {
3 + 5 // kifejezés
3 + 5; // utasítás
}
Feladat: Csinálj utasítást a következő értékekből/értékeket kiszámító kifejezésekből:
- Egy év napjainak száma. - Mikor született az, aki most 18 éves? - Átlagban hány órát kell hetente otthon a progalap gyakorlásával tölteni a szorgalmi időszakban, ha egy kredit (a teljes félév során elvégzett) 30 munkaórát jelent, a félév 20 hétből áll, és ebbe a kreditszámba az órai munka is beleszámít?
Változók használata
Változók deklarációja - Deklaráció: elemek létrehozása
void main(){
// void - Ez egy adatot tárolni képtelen típus, többek között eljárásokhoz használatos
int egesz; // Egy egyszavas (esetünkben 32 bites - 4 bájtos) előjeles egész szám
// Műveletek: +, -, *, /, %, ahol a / az egészosztás egészrésze, a % az egészosztás maradéka
char karakter; // Egy egybájtos, előjeles, vagy előjeltelen egész szám. Jól használható szöveges karakterek tárolására is, ascii-kód segítségével.
float valos1; // Egy egyszavas lebegőpontos szám
// Műveletek: +, -, *, /
double valos2; // Annyiban különbözik a float-tól, hogy 8 bájtos
// Logikai és szöveges típus külön nincs, azokat máshogy kell megvalósítani. (Későbbi gyakorlaton)
}
Változók definiálása - Definíció: a tényleges értékadás
void main(){
int egesz = 2014;
char karakter = 'A'; // Karakterek megadásakor (és mint később látjuk, szövegekben is) használhatunk escape-szekvenciákat, pl.: \" - idézőjel, \' - aposztróf, \t - tabulátor, \n - soremelés (új sor), \r - kocsi-vissza
float valos1 = 3.1415916; // természetesen a float és double is tud tárolni sokkal nagyobb számokat is (ezekről későbbi gyakorlaton lesz szó)
double valos2 = 3.1415916;
}
Feladat: fordítsuk le az alábbit -Wall kapcsolóval! Milyen hibákat kapunk? Javítsuk őket!
#include <stdio.h>
main() {
printf("Hello Világ!\n");
}
Feladat: Futtassuk le az alábbi parancsokat a fenti, javított "Hello World" programon! Mi lett az eredmény és miért? Hogyan tudnánk felcserélni a 2. és 3. parancs eredményét?
$ ./hello ; ./hello $ ./hello && ./hello $ ./hello || ./hello
Input/Output alapok
Kiíratás képernyőre (printf):
#include <stdio.h>
void main(){
/* Változók deklarálása és definiálása */
int egesz = 2014;
char karakter = 'A';
float valos1 = 3.1415916;
double valos2 = 3.1415916;
/* Kiíratás képernyõre */
/* % + "adott típus jelző karakter(ek)" kombináció helyére fog behelyettesítődni, a szöveg után megadott változó értéke. Ügyeljünk a típusra! */
printf("Egyszerű szöveg\n");
printf("Integer érték: %d\n", egesz); // %d - int
printf("Karakter: %c\n", karakter); // %c - char
printf("Float érték: %f\n", valos1); // %f - float
printf("Double érték: %lf\n", valos2); // %lf - double
printf("%s\n", "Szöveg, mint \"string\"\n");
}
C nyelvben a printf függvény formátumsztringjében a "%" és "lf" közé a.b alakban írt két egész számmal vezérelhetjük a valós számok kiíratásának "szélességét" és "pontosságát". Az a jelenti, hogy összesen legalább hány karakteren szélességben legyen kiírva a szám, a b pedig a tizedesjegyek számát jelenti. Ha az a elmarad, akkor a .b alak csak a tizedesjegyek számát fixálja, de minimális "szélességet" nem mond. Pl:
printf("%10.3lf", 42.7); => " 42.700"
printf("%10.0lf", 42.7); => " 43"
printf("%.2lf", 42.7); => "42.70"
Adatok bekérése (scanf):
#include <stdio.h>
void main(){
int egesz;
float valos1;
double valos2;
/* Adatok bekérése */
printf("Adj meg egy egész számot:\n");
scanf("%d", &egesz);
printf("Adj meg két valós számot (Üss Enter-t a két szám bevitele között):\n");
scanf("%f", &valos1);
scanf("%lf", &valos2);
printf("\n\n");
}
Egy kis érdekesség:
#include <stdio.h>
void main(){
// Egész egészként és karakterként
printf("%d\n",65);
printf("%c\n\n",65); // az ASCII tábla szerint a 65. karakter az 'A'
// Valós valósként és egészként
printf("%f\n",65.5555);
printf("%d\n\n",65.5555); // egészként kiíratva egy valós számot nem épp a helyes eredményt kapjuk meg
// Karakter karakterként és egészként
printf("%c\n",'A');
printf("%d\n",'A'); // az ASCII tábla szerint ismét csak az 'A' karakterkódja 65
}
Több adat beolvasása egyszerre:
#include <stdio.h>
void main(){
int st, rd; // egész típusú változók
char nd, th; // karakter típusú változók
printf("Beolvasás (egész karakter egész karakter): ");
scanf("%d%c%d%c", &st, &nd, &rd, &th); // mivel különböző típusok vannak egymás után felsorolva, így gond nélkül bekérhetjük őket egymás után
printf("first = %d; second = '%c'; third = %d; fourth = '%c';\n", st, nd, rd, th);
}
Feladat: Írj egy programot amely bekéri a neved, majd a bekérés után kiírja azt a képernyőre! Ezután bekéri a születési dátumod és kiszámítja a korodat, és ezt is kiírja a képernyőre!
Feladat: Alakítsd át a fenti programot úgy, hogy bekérje a vezetékneved, majd a születési dátumod, majd a keresztneved és végül a korodat! A bekért adatokat "logikus sorrendben" írasd ki a képernyőre!
Blokkok
Blokknak nevezünk két kapcsos zárójel - { } - közé zárt programrészt.
Egy blokkon belül minden változónév csak egyszer használható, egy blokkon belül létrehozott változók csak az adott blokkban használhatók.
void main(){
int szam1;
{
int szam2 = 2;
}
{
int szam3 = 3;
//int szam4 = szam1 + szam2 + szam3; // itt hiba lesz mivel a szam2 nincs deklarálva ebben a blokkban
}
}
Globális és Lokális változók
Globális változó: blokkon kívül deklarált, mindenhol látszik.
Lokális változó: valamilyen blokkon (függvényen) belül deklarált, csak az adott blokkon belül látszik.
#include <stdio.h>
int global = 0;
int LG(int alpha) {
int local = 0;
local += alpha; // local = local + alpha; rövidítése
/*
Az alábbiakra is igaz:
a = a + b, ugyanaz, mint a+=b
a = a - b, ugyanaz, mint a-=b
a = a * b, ugyanaz, mint a*=b
a = a / b, ugyanaz, mint a/=b
*/
global += alpha;
return local;
}
void main() {
printf("Lokalis valtozo: %d, Globális változó: %d\n", LG(2), global);
printf("Lokalis valtozo: %d, Globális változó: %d\n", LG(2), global);
printf("Lokalis valtozo: %d, Globális változó: %d\n", LG(2), global);
}
Feladat: Próbáljuk ki, fordul-e a fenti program, ha a main függvényben megpróbáljuk felhasználni a local változót!
Feladat: Mi történik, ha a global-t csak a fuggveny után deklaráljuk?
Függvényekben és beágyazott blokkokban létrehozhatunk ugyanolyan nevű változót, mint amilyen valamilyen felsőbb szinten már deklarálva van. Ez el fogja rejteni a felsőbb szintű példányt:
#include <stdio.h>
int a = -1;
void fgv() {
printf("%d\n", a);
}
void main() {
int a = 0; // ez elrejti a globális a változót
{
int a = 1; // ez elrejti az eggyel feljebb lévő a változót
{
int a = 2; // ez elrejti az eggyel feljebb lévő a változót
printf("%d\n", a); // 2
}
printf("%d\n", a); // 1
}
printf("%d\n", a); // 0
fgv(); // -1
}
Feladat: Próbáld meg futtatás nélkül megtalálni a hibá(ka)t, majd javítsd (őket)!
int main() {
int elso;
elso = 3;
{
int masodik;
elso = 6
masodik = 5;
}
{
int harmadik;
elso = 9
masodik = 10;
harmadik = 8;
}
masodik = 15;
harmadik = 16;
return 0;
}
Függvények
Deklaráció:
típus név( formális_paraméterek );
Definíció:
típus név( formális_paraméterek ) {
törzs
}
Példa függvények használatára:
#include <stdio.h>
void main(){
// A main függvény allján lévő függvények hívása
nullParamVoid();
nullParamInt();
printf("%d\n\n", negyzetKerulet(5));
int terulet = teglalapTerulet(3,4);
printf("%d\n\n", terulet);
}
void nullParamVoid(){
printf("Ez egy paraméter nélküli függvény, amelynek nincs visszatérési értéke.\n\n");
}
int nullParamInt(){
printf("Ez egy paraméter nélküli függvény, amelynek 1 a visszatérési értéke.\n\n");
return 1;
}
// Egy egyparaméteres fgv, ami kiszámítja egy négyzet kerületét az oldalhosszból
int negyzetKerulet(int a){
int kerulet;
kerulet = 4*a;
return kerulet;
}
// Egy többparaméteres fgv, ami kiszámítja egy téglalap területét a két oldalhosszból
int teglalapTerulet(int a, int b){
return a*b;
}
Feladat: Írj egy függvényt, ami összead két egész értéket, és visszatér az eredménnyel! Írj egy programot is, ami felhasználja ezt!
3. gyakorlat
Mit is tanultunk a 3. gyakorlaton?
Ismerkedés a BÍRÓval
BÍRÓ: itt fogjátok az éles ZH-kat megkapni és feltölteni: https://biro.inf.u-szeged.hu
BÍRÓ2: itt tudjárok kipróbálni az éles ZH-k előtt a BÍRÓ működését: https://biro2.inf.u-szeged.hu
Két BÍRÓ rendszert különböztetünk meg:
Mindkét honlap esetén egy biztonsági kérdés érkezik először! Itt nem kell megijedni. Csak keressük meg a megjelenő oldalon, hogy hogyan kell elfogadni a feltételeket. Fogadjuk el, hogy tisztában vagyunk minden eshetőséggel és lépjünk tovább.
Ezután regisztráljunk mindkét BÍRÓ rendszerben:
A BÍRÓ2-esen már kiosztásra is került az idei próba ZH. Lépjünk be és töltsük le!
Vezérlési szerkezetek
Az if utasítás segítségével valamely tevékenység (utasítás) végrehajtását egy kifejezés (feltétel) értékétől tehetjük függővé. Az if alábbi formájában az utasítás csak akkor hajtódik végre, ha a kifejezés értéke nem nulla (igaz):
if (kifejezés){
utasítás
}
Az if-else szerkezet használatával arra az esetre is megadhatunk egy tevékenységet, amikor a kifejezés (feltétel) értéke zérus (hamis):
if (kifejezés){
utasítás
} else {
utasítás
}
Az else-if szerkezet nem más, mint egymásba ágyazott if utasítások egy gyakran használt formája, amikor az else ágakban szerepel az újabb if utasítás:
if (kifejezés){
utasítás
} else if (kifejezés){
utasítás
} else if (kifejezés){
utasítás
} else {
utasítás
}
A zárójelben lévő kifejezés egy logikai kifejezést takar. Ezt a program a szelekciós vezérlőszerkezet végrehajtásakor kiértékeli, és a kiértékelés eredménye vagy igaz vagy hamis érték. Egy logikai kifejezés logikai változókból/értékekből és logikai operátorokból állhat. A C nyelvben nincs külön logikai típus, egész típusokban (int, char) tárolhatunk logikai értékeket: a 0 jelenti a hamisat, a nem nulla pedig az igazat (ez gyakran 1, DE fordítója válogatja). Logikai értékek keletkezhetnek relációs operátorok használatával, ezek két érték összehasonlítására használhatók
Relációs operátorok:
a és b értékek: változók, konstansok, valamilyen művelet vagy függvény eredményei, vagy literálok (literálnak nevezzük a helyben definiált adatot, pl. 5, vagy 'A')
a == b - a egyenlő-e b-vel. HA FELTÉTELESEN KÉRDEZÜNK RÁ MINDIG == KELL! a < b - a kisebb-e b-nél a > b - a nagyobb-e b-nél a <= b - a kisebb-egyenlő-e b-nél a >= b - a nagyobb-egyenlő-e b-nél a != b - a nem egyenlő-e b-vel
Logikai operátorok:
a és b logikai értékek: változók, konstansok, valamilyen művelet vagy függvény eredményei, vagy literálok
!a - a kifejezés értéke NEM a, tehát akkor lesz igaz, ha a hamis volt a && b - a kifejezés értéke a ÉS b, tehát akkor lesz igaz, ha a és b is igaz volt a || b - a kifejezés értéke a VAGY b, tehát akkor lesz igaz, ha a és b közül legalább az egyik igaz volt
Feladat: Készíts egy programot, ami bekér egy egész számot és kiírja, hogy az adott szám páros vagy páratlan-e. ( Emlékeztető: egész számok esetén az osztási maradékot a % jel segítségével kapjuk meg )
#include <stdio.h>
int main() {
int x;
printf("Kérek egy egész számot:");
scanf("%d", &x);
if (x%2 == 0) // megnézzük, hogy az x 2-vel való osztása után a maradék értéke 0-e, ha igen akkor az azt jelenti, hogy x osztható 2-vel, tehát páros
printf("A megadott szám páros.\n");
else // ellenkező esetben a maradék nem 0, tehát nem osztható maradék nélkül 2-vel, tehát páratlan
printf("A megadott szám páratlan.\n");
return 0;
}
Feladat: Módosítsuk most a programot úgy, hogy két egész számot kérjen be a program majd írja ki, hogy az első szám osztható-e a másodikkal.
Feladat: Próbáljuk ki, mi történik, ha a második szám 0! Javítsuk a programot!
Feladat: Használjunk többszörös szelekciót! ( else-if )
Feladat: Írjuk meg if nélkül a fenti programot. (megoldás a következő anyagrészben: feltételes kifejezések)
Feltételes kifejezések
Az if-else helyett feltételes kifejezések is használhatók. Ezzel tömörebb formában fogalmazható meg ugyanaz, sőt, egy adott helyre behelyettesítendő érték kiválasztására is használható.
feltetel ? muvelet ha igaz : muvelet ha hamis;
Feladat: Írjuk ki egyetlen printf segítségével, hogy egy szám páros vagy páratlan-e!
#include <stdio.h>
int main() {
int x;
printf("Kérek egy egész számot: ");
scanf("%d", &x);
// tehát ez a sor ugyanazt eredményezi, mint az előző részben az if-else használatával, csupán egyszerűbb a kivitelezés
printf("A szám %s.\n", (x%2 == 0) ? "páros" : "páratlan");
return 0;
}
PÉLDA: a feltételes kifejezéseket egymásba is ágyazhatjuk:
#include <stdio.h>
int main () {
int x;
printf("Kérek egy egész számot: ");
scanf("%d", &x);
printf("Kérek egy másik számot: ");
scanf("%d", &y);
printf("Osztoja-e %d-nek %d?. %s\n", x, y, // %s helyére string-ek, azaz karaktersorozatok helyettesíthetők be, így pl. bármilyen szöveg "" között megadva
(y==0) ? "A kerdes ertelmetlen!" : // tehát leellenőrizzük, hogy y nulla-e, ha igen, akkor kiírjuk, hogy értelmetlen,
((x%y==0) ? "Igen, osztója." : "Nem, nem osztója.") // ellenkező esetben, pedig az x és y oszthatóságától függően kerül kiírásra az 1. avagy a 2. állítás
);
}
Esetkiválasztásos szelekciós vezérlés
A switch utasítás többirányú programelágaztatást tesz lehetővé olyan esetekben, amikor egy egész kifejezés értékét több konstans értékkel kell összehasonlítanuk. Az utasítás álatlános alakja:
switch ( kifejezés ) {
case címke : műveletek; break;
case címke : műveletek; break;
...
case címke : műveletek; break;
default : műveletek; break;
}
A switch utasítás először kiértékeli a kifejezést, majd átadja a vezértlést arra a case címkére (esetre), amelyben a címke értéke megegyezik a kiértékelt kifejezés értékével - a futás ettől a ponttól folytatódik. Amennyiben egyik case sem egyezik meg a kifejezés értékével, a program futása a default címkével megjelölt utasítással folytatódik. Ha nem használunk default címkét, akkor a vezérlés a switch utasítás blokkját záró } utáni utasításra adódik. A break utasítás kiugrasztja a switchből a vezérlést, ha kihagyjuk, az a következő címkére kerül. Ez használható pl. olyankor, ha több címkéhez ugyanazokat a műveleteket kell végrehajtani.
Feladat: Írjunk egy függvényt, ami egy "x" egész számot kap paraméterként és kiírja, hogy a hét x. napja milyen nap.
void hetnapja_if (short int x) {
if (x==1) {
printf("Hétfő\n");
} else if (x==2) {
printf("Kedd\n");
} else if (x==3) {
printf("Szerda\n");
} else if (x==4) {
printf("Csütörtök\n");
} else if (x==5) {
printf("Péntek\n");
} else if (x==6) {
printf("Szombat\n");
} else if (x==7) {
printf("Vasárnap\n");
} else
printf("Hiba! x értéke legalább 1 és legfeljebb 7 lehet!\n");
}
Feladat: Írjuk meg ugyanezt az fgv-t switch használatával.
void hetnapja_switch (short int x) {
switch (x) { // tehát ez a sor miatt az x kerül összehasonlításra a case-ek után megadott értékkel
case 1: // amennyiben valamelyik case-re ráillik az x, akkor az azon belül lévő utasítás sorozat fog lefutni
printf("Hétfő\n");
break; // ha break-kel zárjuk le a case-t, akkor akár ráillene a többi eset valamelyikére az x, akár nem, mindenképp ki fog lépni a switch-ből
case 2:
printf("Kedd\n");
break;
case 3:
printf("Szerda\n");
break;
case 4:
printf("Csütörtök\n");
break;
case 5:
printf("Péntek\n");
break;
case 6:
printf("Szombat\n");
break;
case 7:
printf("Vasárnap\n");
break;
default: // tehát, ha egyik esetre sem illet rá az x, akkor a default fog lefutni (melynek megadása NEM kötelező)
printf("Hiba! x értéke legalább 1 és legfeljebb 7 lehet!\n");
}
}
Feladat: Írjuk meg a main-t is a fenti függvény használatához.
Kezdőfeltételes ismétléses vezérlés
Más néven előltesztelős ciklus, vagy while-ciklus.
A while ciklus mindaddig ismétli a hozzá tartozó utasítást (a ciklus törzsét), amíg a vizsgált kifejezés (vezérlőfeltétel) értéke igaz (nem nulla). A vizsgálat mindig megelőzi az utasítás végrehajtását. Általános alakja:
while (kifejezés){
utasítás
}
Tehát, ha a kifejezés értéke már kezdetben is hamis, akkor a while ciklus törzsén belül elhelyezkedő utasítás(ok) egyszer sem fut(nak) le.
A switch-nél látott break utasítás ciklusokból való "kitörésre" is alkalmazható. Így írhatunk olyan (esetleg egyébként végtelen) ciklust, amelyben egy, vagy több helyen egy if segítségével megvizsgálunk egy feltételt, majd az alapján (esetleg néhány művelet végrehajtása után) kilépünk a ciklusból. Ezt hívjuk hurok ismétléses vezérlésnek.
Feladat: Írjunk egy programot, ami kiírja 1-től 10-ig számokat.
#include <stdio.h>
int main() {
int i = 1;
while (i<=10) { // tehát addig fog futni a ciklus, amíg az i értéke kisebb vagy egyenlő, mint 10
printf("%d\n", i);
i = i + 1; // FONTOS! Ne felejtsük el növelni az i értékét, különben a ciklus futásának soha sem lesz vége, azt nevezzük VÉGTELEN CIKLUSnak
}
return 0;
}
Feladat: Írjunk olyan programot, ami addig kér be számokat a billentyűzetről, amíg a beírt szám nem 0! (0 az adott végjel)
Feladat: Módosítsuk a programot úgy, hogy végeredményként írja ki a beírt számok összegét!
Végfeltételes ismétléses vezérlés
Más néven hátultesztelős ciklus, vagy do-while-ciklus.
A do-while utasításban a ciklus törzsét képező utasítás végrehajtása után kerül sor a tesztelésre. Így a ciklus törzse legalább egyszer mindig végrehajtódik. Általános alakja:
do {
utasitas
} while (kifejezés);
A do-while ciklus futása során mindig először végrehajtódik az utasítás és ezt követően értékelődik ki a kifejezés. Amennyiben a kifejezés értéke igaz (nem nulla), akkor új iteráció kezdődik (azaz újra lefut a ciklus), míg hamis (0) érték esetén a ciklus befejezi működését.
Feladat: Írjunk egy olyan programot 'do-while' ciklus segítségével, ami 0 végjelig kér be számokat, majd kírja azok összegét. A ciklusban ne szerepeljen a 'break' utasítás.
#include <stdio.h>
int main() {
int x, osszeg=0;
do {
printf("Kérek egy számot (kilépéshez: 0):");
scanf("%d", &x);
osszeg+=x;
} while (x!=0); // tehát mindig a ciklus végén történik az ellenőrzés
printf("A számok összege: %d\n", osszeg);
return 0;
}
Számlálásos ismétléses vezérlés
Más néven for-ciklus.
A for utasítást átlagban akkor használjuk, ha a ciklusmagban megadott utasítást adott számszor kívánjuk végrehajtani. Általános alakja:
for (kezdőérték_kifejezés ; feltétel_kifejezés ; léptető_kifejezés){
utasítás
}
A ciklusban van egy változó (a ciklusváltozó, vagy számláló), amit egy kezdőértékből kiindulva, folyamatosan növelünk vagy csökkentünk egy végértékig, és minden ilyen körben végrehajtunk néhány műveletet. A műveletekben a ciklusváltozó aktuális értéke is felhasználható.
A kezdőérték_kifejezés-sel állítjuk be a ciklusváltozó kezdőértékét. A léptető_kifejezés-sel növeljük vagy csökkentjük a ciklusváltozó értékét tetszés szerint. A feltétel_kifejezés-t pedig minden egyes iterációban ellenőrizzük. A ciklus addig fut amíg ez a feltétel teljesül. Mivel a feltételünk akkor nem fog már teljesülni, amikor a ciklusváltozó elér egy bizonyos értéket, ezért jól befolyásolható, hogy a ciklus hányszor fusson le.
A ciklus változó növelésére (inkrementálására) és csökkentésére (dekrementálására) létezik egy-egy speciális operátor:
A++; => A = A + 1; (avagy A += 1;) A--; => A = A - 1; (avagy A -= 1;) Postfix és prefix alakban is használhatóak, jelentésük kicsit különböző: B = A++; => 1. B = A; 2. A = A + 1; Tehát B A korábbi értékét kapja. B = ++A; => 1. A = A + 1; 2. B = A; Tehát B A megnövelt értékét kapja meg. Ugyanez érvényes a -- operátorra is.
Feladat: Írjunk egy programot, ami összeszorozza 1-10-ig a számokat.
#include <stdio.h>
int main() {
int i;
int szorzat;
// több kezdő érték illetve léptető kifejezés is megadható vesszővel elválasztva
for (i=1, szorzat=1; i<=10; ++i) { // tehát i=1 miatt 1-től indul a számlálás és egész addig tart amíg i el nem éri a 10-es értéket, az i<=10 miatt
szorzat*=i; // és minden egyes körben képezzük az szorzatot az adott i-vel (1*2*3*...*10)
}
printf("A számok szorzata: %d\n", szorzat);
return 0;
}
Feladat: Módosítsuk a for ciklust úgy, hogy csak minden 3-mal osztható számot szorozzon össze!
#include <stdio.h>
int main() {
int i;
int szorzat;
for (i=3, szorzat=1; i<=10; i+=3) // csak annyit kell tennünk, hogy az i értékét nem 1-gyel, hanem 3-mal növeljük minden egyes körben 3-tól indítva
szorzat*=i; // mivel 3 ugyebár osztható 3-mal, ezért ha mindig 3-mal nő i, így mindig a 3-mal osztható számokat kapjuk, egész a végfeltételig (jelen esetben 10-ig)
printf("A számok szorzata: %d\n", szorzat);
return 0;
}
Feladat: Próbáljuk ki mit csinál az alábbi for ciklus:
int i,j,output;
for (i=1, j=100, output=0; i<=10; i++, j--)
output+=i*j;
Feladat: Módosítsuk a ciklusmagot úgy, hogy egy printf segítségével kiírjuk az i,j és out aktuális értékét.
Plusz pontos órai feladat ...
Jövő héten 1. miniZH (2017.09.25.)
Téma:
C programozás (ezután mindig az lesz): 2. gyakorlat anyaga.
Gyakorlásra:
A honlapomon a 2. gyakorlathoz tartozó anyag, magyarázatokkal, példákkal.
A honlapom mellett további feladatok találhatóak a PUB-ban. (/n/pub/ProgramozasAlapjai/Gyakorlat/ - erős átfedés van az "itt" és "ott" található feladatok között).
Jelmondat: "Nézegetéssel nem lehet programozni tanulni, csak gyakorlással!"
Összegzésként:
Tudni kellene C programot fordítani és futtatni. ( "gcc -o vmi vmi.c" ÉS "./vmi" )
Létrehozni változókat. ( int, char, float, double )
Beolvasni adatot. ( scanf )
Kiíratni adatot. ( printf )
Visszatérési értékekre ügyelni. ( return )
Csak azt programozzuk le amit a feladat kér, se többet, se kevesebbet. Pl. ha sortörést kér, teszünk sortörést, ha nem írja, hogy ki kellene írni valamit a képernyőre, nem íratunk ki semmit, stb.
( Az itt felsoroltakra a további ZH-kon is mind szükség lesz. )
Egyéb infó:
Előreláthatóan 30 percetek lesz a feladatok megoldására és beadására (tehát hétfő reggeli csoport - 8:40-ig/ hétfő délutáni csoport - 15:30-ig). A feladatokat a BÍRÓ rendszeren keresztül fogjátok megkapni és beadni is, és az értékelést is a bíró fogja csinálni ott helyben. Tehát egyből látni fogjátok a pontszámokat amiket a bíró adott. Limitált a feltöltések száma és mindig a legjobb pontszám fog számítani. Aki késik, az is csak a fenti időintervallum alatt írhatja a ZH-t, mivel a bíró rendszer nyit, majd automatikusan zár is. Hiányozni csak igazolással lehet, de a ZH akkor sem pótolható!
4. gyakorlat
Mit is tanultunk a 3. gyakorlaton?
Ismétlő feladat:
(a) Kérj be 1 és 10 között egy számot az alapjan, hogy mennyit gyakorolja az adott hallgató hetente a progalapot.
Ha a szám >=5 és <=10, akkor eleget tanul a hallgató, <5 esetén többet kellene, ha > 10 akkor írjuk ki, hogy helytelen adat vagy csak nagyon jó a hallgató.
(b) Kérdezz rá, hogy szeretné-e a hallgató megváltoztatni a megadott számot (y/n). Írasd ki, hogy mit választott a hallgató.
(c) Kezdőfeltételes ismétléses vezérléssel kérj be 5 számot (tehát ciklusonként 1 számot).
(d) Számlálásos ismétléses vezérléssel oldd meg egy 5*5-ös táblázat kiíratását.
(e) Majd egy végfeltételes ismétléses vezérlést addig futtass, amíg nem kap a program egy SPACE karaktert.
Megoldás letöltése .
#define macro
Fordítási időben jönnek létre. A konstansdefiniálás általános alakja:
#define NÉV érték
Az érték bármi lehet, később a fordítás első menetében a preprocesszor a név összes előfordulását szövegesen a megadott értékkel helyettesíti, majd folytatódik a fordítás. Így a konstans értéke akár programrészlet is lehet.
Feladat: Írj egy programot, ami 1-től 10-ig kiírja a számokat, majd minden második, majd minden negyedik számot.
#include <stdio.h>
int main() {
int i;
for(i=1; i<=10; i++) {
printf(" %d", i);
}
putchar('\n');
for(i=1; i<=10; i+=2) {
printf(" %d", i);
}
putchar('\n');
for(i=1; i<=10; i+=4) {
printf(" %d", i);
}
putchar('\n');
return 0;
}
Feladat: Módosítsuk úgy a programot, hogy 21-től 144-ig írjon ki. Hány helyen kellett átírnunk számokat?
Feladat: Csináljuk meg ugyanezt konstansokkal. Így hány helyen kellene módosítani?
#include <stdio.h>
#define A 1
#define B 10
int main() {
int i;
for(i=A; i<=B; i++) {
printf(" %d", i);
}
putchar('\n');
for(i=A; i<=B; i+=2) {
printf(" %d", i);
}
putchar('\n');
for(i=A; i<=B; i+=4) {
printf(" %d", i);
}
putchar('\n');
return 0;
}
Elmélkedjünk: Mi lesz az eredménye az alábbinak:
#include <stdio.h>
#define int 100.0
int main() {
float f=int;
printf("%f\n", f);
return 0;
}
Tömbök
Több azonos típusú adat egyben való tárolására és kezelésére alkalmazhatók a tömbök.
Általános alakja:
típus név[méret];
pl.: 10 darab egész érték tárolására egy tömb létrehozása:
int tomb[10];
A tömbök egyes elemeire egy úgynevezett index-szel lehet hivatkozni. Ez maga a tömben lévő elemek sorszáma. A tömbben az elemek sorszámozása 0-tól kezdődik. Tehát a tömben lévő első elem indexe 0. Ennek megfelelően egy N méretű tömb utolsó eleme az N-1. indexen érhető el. Ha végig szeretnénk haladni a tömb elemein egy ciklussal, akkor az indexek helyes kezelésére nagy figyelmet kell fordítani.
Feladat: Készíts egy 10 egész szám tárolására alkalmas tömböt. Töltsd fel az 1..10 értékekkel, majd írasd ki az elemeit.
#include <stdio.h>
#define N 10
#define M 10
int main()
{
int tomb[N]; // létrehozzuk az N méretű, 1 dimenziós tömböt
int i;
for(i=0; i<M; i++) {
tomb[i]=i+1; // megyünk sorba a tömb elemein és mindnek értéket adunk, méghozzá a folyamatosan növő i értékét kapják meg a tömb elemei egymás után
}
for(i=0; i<M; i++) {
printf(" %d", tomb[i]);
}
return 0;
}
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|
Az első sor prezentálná a tömb elemek indexeit, a második sor pedig az adott indexhez tartozó tömb elem által tárolt értéket.
Kérdés: Mi történik, ha M<N? És ha N<M?
Egy tömbnek az alábbi módon is adhatunk értéket:
int tomb[10] = {2,3,4,7,6,5,10,9,8,1};
Feladat: Készíts egy 3x3-as mátrixot, töltsd fel elemekkel, majd írasd ki az elemeit sor illetve oszlopfolytonosan is!
#include <stdio.h>
#define N 3
int main()
{
int tomb[N][N];
int i, j;
for(i=0; i<N; i++) { // a különböző ciklusokat egybásba is lehet ágyazni
for(j=0; j<N; j++) { // jelen esetben N-szer fog lefutni a külső for ciklus
scanf("%d", &(tomb[i][j])); // és (N*N)-szer a belső
}
}
for(i=0; i<N; i++) { // természetesen itt is és a következőnél is ugyanaz a helyzet
for(j=0; j<N; j++) {
printf("%d", tomb[i][j]);
}
}
for(i=0; i<N; i++) {
for(j=0; j<N; j++) {
printf("%d", tomb[j][i]);
}
}
return 0;
}
Feladat: Írj egy függvényt, ami egy egész tömböt kap paraméterül és lecseréli benne az elemeket az abszolútértékükre. A tömb kiírását szintén függvény végezze!
#include <stdio.h>
#define N 10
void tombabs(int tomb[], int meret) {
int i;
for(i=0; i<meret; i++) {
if(tomb[i<0) { // ha kisebb, mint 0, akkor negatív, tehát negálunk
tomb[i] = -tomb[i];
}
}
}
void kiir(int tomb[], int meret) {
int i;
for(i=0; i<meret; i++) {
printf(" %d", tomb[i]);
}
putchar('\n');
}
int main()
{
int i, T[N], e=1;
for(i=0; i<N; i++) { // a tömb feltöltése változó értékekkel
T[i]=e;
e *= -2;
}
kiir(T, N); // a jelenlegi tartalom kiíratása
tombabs(T, N); // abszolút értéket számoló függvény meghívása
kiir(T, N); // tömb ismételt kiíratása, immáron az abszolút értékekkel
return 0;
}
Karaktertömbök - "stringek"
Mivel C-ben nincs külön string típus, ezért karaktertömbök megvalósításával helyettesíthetjük őket.
Lényegileg ugyanolyan, mint egy sima tömb, csak egy karaktersorozatot fogunk benne letárolni,
és az előzőekhez hasonlóan hivatkozhatunk minden egyes karakterre a megadott szövegünkben,
külön-külön is akár, részekre bontva.
A szöveg végét egy 0 ascii kódú karakter (a NULL karakter)
jelzi. Ez a karakter a tömbön belül bárhol lehet, az utána levő tömbelemeket a sztringeket feldolgozó
függvények figyelmen kívül hagyják, így egy tömbben bármilyen hosszúságú szöveg tárolható,
ami rövidebb a tömb méreténél. A záró 0 karakternek mindenképpen szerepelnie kell,
mivel a tömb mérete nem állapítható meg, így nem lehetne tudni meddig tart a szöveg a memóriában.
Emiatt eggyel nagyobb méretű tömböt kell deklarálni szöveg tárolására, hogy a záró 0 elférjen
Tehát, hogy is nézne ez ki a létrehozás:
char karaktertomb[10]; // 10 karakter tárolására elegendő karaktertömb
Tételezzük fel, hogy feltöltöttük a tömbünket, mi van most benne? Mondjuk benne van a "PROGALAP" szó:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
P | R | O | G | A | L | A | P | 0 | ? |
Ahol a kérdőjelek definiálatlan, véletlenszerű adatot jelentenek.
Feladat: Deklarálj egy megfelelő hosszúságú karaktertömböt, majd írd bele a "Hello Vilag!" szöveget! Írasd ki az str értékét kétféleképpen!
#include <stdio.h>
int main()
{
char str[20];
str="Hello Vilag!";
printf("%s", str);
return 0;
}
Ez így fordítási hiba. A baj az, hogy str egy karaktertömb, tehát egyesével lehet feltölteni az elemeit. Vagy használhatjuk az strcpy() függvényt.
#include <stdio.h>
#include <string.h>
int main()
{
char str[20];
strcpy(str, "Hello Vilag!");
printf("%s", str);
printf(str);
return 0;
}
Feladat: Ha tudjuk, hogy egy karaktersorozatot a 0 karakter zár, akkor módosítsd a programot úgy, hogy a következő sorba csak a "Hello" szöveget írja ki!
Feladat: Készíts egy programot, amely beolvas egy legfeljebb 255 karakter hosszú sztringet, majd kiírja a sztringben található számjegyek számát.
Nézzünk néhány érdekesebb példát:
Feladat:
Buborék rendezés. Vegyünk egy 1D tömböt, amelybe 10 számjegyet olvasunk be, tetszőleges sorrendben. A buborék rendezés elve, hogy haladunk végig a tömbön és egyesével hasonlítgatjuk össze az egymás mellett lévő tömb elemeket. Ha az alacsonyabb indexű elem nagyobb, mint a mellette levő, magasabb indexű, akkor cseréljük ki őket. Így járjunk el egészen addig, amíg nagyság szerinti sorrendbe nem lesz rendezve a tömbünk.
Megoldás letöltése .
Feladat:
Fibonacci sorozat. A rákövetkező szám mindig az őt megelőző kettő összege. 0,1,1,2,3,5,8,13,21,34,55,89,...
Megoldás letöltése .
Feladat: Bővítsük úgy az előbbi feladatot, hogy ne a képernyőre íródjon ki az adott számot, hanem egy tömbbe kerüljenek a sorozat elemei, egymás után, majd az így kapott tömb tartalmát írjuk ki a program végén.
Megoldás letöltése .
Jövő héten 2. miniZH (2017.10.02.)
Téma:
3. gyakorlat anyaga. (De tudni kell az azt megelőző gyakorlatok anyagát is!)
Gyakorlásra:
A honlapomon lévő anyag, magyarázatokkal, példákkal.
A honlapom mellett további feladatok találhatóak a PUB-ban. (/n/pub/ProgramozasAlapjai/Gyakorlat/ - erős átfedés van az "itt" és "ott" található feladatok között).
Egyéb infó:
Előreláthatóan 30 percetek lesz a feladatok megoldására és beadására (tehát 8:40-ig/15:30-ig). A feladatokat a BÍRÓ rendszeren keresztül fogjátok megkapni és beadni is, és az értékelést is a bíró fogja csinálni ott helyben. Tehát egyből látni fogjátok a pontszámokat amiket a bíró adott. Aki késik, az is csak a fenti időintervallum alatt írhatja a ZH-t (a bíró rendszer nyit, majd automatikusan zár is). Hiányozni csak igazolással lehet, de a ZH akkor sem pótolható!
5. gyakorlat
Karakter, Float és Integer típusról komolyabban
sizeof operátor - típusok méretének meghatározása byteokban. Pl.:
int i = sizeof(int); // ilyenkor az i változóba bele kerül az int típus mérete. ez a C esetén 4 byte
A legtöbb típust mind meglehet adni előjeles vagy előjeltelen formában. Egy típust előjelessé a signed, előjeltelenné az unsigned módosítóval tehetünk. A típusok alapértelmezés szerint általában előjelesek.
C típus méret(bájt) alsó határ felső határ _______________________________________________________ char 1 ? ? signed char 1 -128 127 unsigned char 1 0 255 short int 2 -32768 32767 unsigned short int 2 0 65535 int 4 -2147483648 2147483647 unsigned int 4 0 4294967295 long int 4 -2147483648 2147483647 unsigned long int 4 0 4294967295 long long 8 -263 263-1 float 4 -+3.4028234663852886E+38 double 8 -+1.7976931348623157E+308 long double 8 -+1.7976931348623157E+308
Túlcsordulás: Ha egy művelet eredménye túlhalad valamelyik irányban az azt eltárolni próbáló változó értékkészletén, akkor túlcsordulásról beszélünk. (Ha lefelé halad túl, szoktuk alulcsordulásnak is hívni.) Ilyenkor a "számláló körbefordul", tehát ha pl. egy unsigned char változóhoz, aminek az értéke 250 hozzáadunk 10-et, a változó értéke 4 lesz. ( Mivel a char típus mérete 1 byte = 8 bit. 250 + 10 = 260 bit. 5 bit a túlcsordulás. )
F: Hány bájton tárolódik a char/int/float/double típus?
==============================================================================
#include <stdio.h>
int main() {
printf("char: %d\n", sizeof(char));
printf("int: %d\n", sizeof(int));
printf("float: %d\n", sizeof(float));
printf("double: %d\n", sizeof(double));
return 0;
}
Feladat:: Próbáljuk ki a többi típusra is, hogy melyik hány bájton tárolódik.
F: Írasd ki a 64 és 95 közé eső kódú karaktereket.
==============================================================================
#include <stdio.h>
int main() {
char c;
for(c=64; c<96; c++) {
printf(" %c", c);
}
putchar('\n');
return 0;
}
Feladat: Alakítsd át az előző példát: írasd ki az 'a' és 'z' közé eső karakterek kódjait.
F: Mi a különbség a signed char és az unsigned char értékkészlete között?
Írasd ki -128-tól 255-ig egy signed és egy unsigned char típusú változó
számértékét!
==============================================================================
#include <stdio.h>
int main() {
int i;
signed char sc;
unsigned char uc;
for(i=-128; i<=255; i++) {
sc=i;
uc=i;
printf("%d %d\n", sc, uc);
}
return 0;
}
Feladat: Számoljuk meg egy karaktertömbben, hogy hány féle különböző karaktert tartalmaz.
Megoldás letöltése: kulonbozoKarakterek.c, illetve online verziója:
#include <stdio.h>
int main() {
char c;
int i=0, ossz = 0;
char tomb[7] = {'p','a','r','a','f','a', 0};
for(c='a'; c<='z'; c++) { // a-tol z-ig vegig iteralunk
i=0; // mindig visszaallitjuk az i erteket 0-ra,
// hogy amikor a kovetkezo beture lepunk az ABC-ben, mindig a tomb elejetol kezdje el ismet keresni
while(tomb[i] != 0){ // 0 jelig megyunk
if (tomb[i] == c){ // ha benne van a karakter
ossz++; // noveljuk az ossz-ta
break; // es kiugrunk a ciklusbol, mert nem akarjuk a tobbi egyezest is nezni ugyanarra a beture
}
i++; // VEGTELEN CIKLUS ELLEN lepegetnuk vegig egyessevel
}
}
printf("Kulonbozo karakterek szama: %d\n", ossz);
return 0;
}
F: Mi a különbség a float és a double pontossága között? Add hozzá az 1, 0.1,
0.01, 0.001, ... sorozat elemeit egy-egy float és double változóhoz. Milyen
értékeket kapsz lépésenként?
==============================================================================
#include <stdio.h>
int main() {
int i;
float f = 0.0, df = 1.0;
double d = 0.0, dd = 1.0;
for(i=0; i<20; i++) {
f += df;
d += dd;
df *= 0.1;
dd *= 0.1;
printf("%d: float: %22.20f; double: %22.20lf\n", i + 1, f, d);
}
return 0;
}
F: Deklarálj egy egész, egy valós és egy karakter változót. Mindhármat írasd
ki mindhárom formában! Figyeld meg a fordítási warningokat!
Warning-ok kiíratása -Wall kapcsolóval történik. pl. "gcc -Wall -o prog prog.c"
==============================================================================
#include <stdio.h>
void main(){
int a = 65;
float b = 65.5555;
char c = 'A';
// Egész egészként és karakterként
printf("%d\n",a);
printf("%c\n\n",a); // az ASCII tábla szerint a 65. karakter az 'A'
// Valós valósként és egészként
printf("%f\n",b);
printf("%d\n\n",b); // egészként kiíratva egy valós számot nem épp a helyes eredményt kapjuk meg
// Karakter karakterként és egészként
printf("%c\n",c);
printf("%d\n",c); // az ASCII tábla szerint ismét csak az 'A' karakterkódja 65
}
Hibajavítás
Feladat: Fordítsuk le az alábbi kódot és javítsuk ki a terminal hibaüzeneteit felhasználva. Miután egy hiba sem maradt, fordítsuk le a -Wall kapcsolót használva is és folytassuk a javítást. pl.: "gcc -Wall -o prog prog.c"
#include <stdio.h>
typedef struct pont {
double x;
double y;
double z;
} pont_t;
int Main(int argc, char *argv[]) {
pont_t P[4];
double t[4];
double felszin;
if (argc < 5) {
fprintf(stderr, "Használat: %c '(x1,y1,z1)' '(x2,y2,z2)' '(x3,y3,z3)' '(x4,y4,z4)'\n", argv[0]);
return 1;
}
for (i = 0; i < 4; ++i) {
P(i) = get_pont(argv[i+1]);
t(i) = 0.0;
}
t[0] = terulet(P[1], P[2], P[3]);
t[1] = terulet(P[0], P[2], P[3]);
t[2] = terulet(P[0], P[1], P[3]);
t[3] = terulet(P[0], P[1], P[2]);
for (i = 0; i < 4; ++i) {
if (t[i] = 0.0)
printf("A megadott pontok egy síkba esnek.\n");
return 1;
}
}
for (i = 0; i < 4; ++i) {
felszin += t{i};
}
printf("A test felszíne: A = %.3lf\n", felszin);
return 0;
}
pont_t get_pont(const char *str) {
pont_t retval = {0.0, 0.0, 0.0};
sscanf(str, "(%lf,%lf,%lf)", retval.x, retval.y, retval.z);
}
double tavolsag[pont_t P, pont_t Q] {
pont_t d;
d.x = P.x - Q.x;
d.y = P.y - Q.y;
d.z = P.z - Q.z;
return sqrt(d.x * d.x + d.y * d.y + d.z * d.z);
}
double terulet(pont_t A, pont_t B, pont_t C) [
double a, b, c, s;
a = tavolsag(B, C);
b = tavolsag(A, C);
c = tavolsag(A, B);
s = (a + b + c) / 2.0;
return sqrt(s * (s - a) * (s - b) * (s - c));
]
Nézzünk néhány érdekesebb példát:
(a) Írjunk egy ciklust, amely végig ellenőrzi egy bemenetként kapott n szám esetén, hogy 1-től 1000-ig mely számok oszthatóak n^2+n+1-gyel.
(b) Keressük meg egy tömb legnagyobb/legkisebb elemét.
(c) Írjunk egy függvényt, melynek legyen 4 egész értékű bemeneti paramétere. Az 1. legyen egy 1D int tömb. A 2. a tömb mérete. A 3. és a 4. egy-egy adott egész érték. A 3. parameterkent megadott erteku tomb elemet irjuk felul a 4. parameterkent megadott ertekkel.
(d) A (c) feladat módosítása úgy, hogy a 3. paraméterben megadott indexszű elemet cseréljük ki, a 4. paraméterben megadott indexszű elem értékére. Tehát a 3. elemet a 4.-re, a 4.-et pedig a 3.-ra.
(e) Hozzunk létre egy integer 2D tömböt és töltsük fel a szorzótábla értékeivel.
(f) Hozzunk létre egy karakter tömböt. Töltsük fel tetszőlegesen és írassuk ki 2 különböző módon. Ellenőrizzük le a hosszát is 2 különböző módon. Toljuk el egyesével az összes karakter értékét 1-gyel.
Megoldás letöltése: gyak05.c, illetve online verziója:
#include <stdio.h>
/**
Integer tomb kiiratasa
*/
void kiirIntegerTomb(int tomb[], int meret){
int i;
for (i=0; i<meret; i++){
printf("%4d", tomb[i]);
}
printf("\n");
}
/**
Char tomb kiiratasa
*/
void kiirCharTomb(char tomb[]){
int i = 0;
while (tomb[i] != 0){
printf("%c", tomb[i]);
i++; // INDEXVALTOZO NOVELESE, HOGY NE LEGYEN VEGTELEN CIKLUS
}
printf("\n");
}
/**
(a) Írjunk egy ciklust, amely végig ellenorzi egy bemenetként kapott n szám esetén, hogy 1-tol 1000-ig mely számok oszthatóak n^2+n+1-gyel
*/
int fgvA(int n){
int i;
for (i=1;i<1000;i++){ // vegig iteralunk 1-tol 1000-ig
if (i%(n*n+n+1) == 0) { // mindig az aktualis elemet elosszuk a megadott kifejezessel es vesszuk a maradekot
// amennyiben a maradek 0, akkor oszthatoak
printf("%4d\n", i);
}
}
return 0; // mivel nem volt megadva, hogy mi legyen a visszateresi ertek, ezert 0-t adunk vissza
}
/**
(b) Keressük meg egy tömb legnagyobb/legkisebb elemét. Ez az elem legyen a fuggveny visszateresi erteke.
(Itt most epp legkisebb. Legnagyobb eseten csak meg kellene forditani a relacios jelet az if soraban.)
*/
int fgvB(int tomb[], int meret){
int min, i;
min = tomb[0]; // kimentjuk a tomb elso (0.) elemet
for (i=1;i<meret;i++){ // vegig iteralunk rajta (A 0. elemet mar kivettuk, szoval az 1.-tol megyunk tovabb, egeszen a vegeig)
if (tomb[i] < min){ // amennyiben a tomb i-edik (azaz az aktualis) eleme kisebb, mint a min erteke
min = tomb[i]; // akkor felulirjuk a min-t vele
}
}
return min; // a legkisebb elem a visszateresi ertek
}
/**
(c) Írjunk egy függvényt, melynek legyen 4 egész értéku bemeneti paramétere.
Az 1. legyen egy 1D int tömb. A 2. a tömb mérete. A 3. és a 4. egy-egy adott egész érték.
A 3. parameterkent megadott erteku tomb elemet irjuk felul a 4. parameterkent megadott ertekkel.
*/
void fgvC(int tomb[], int meret, int a, int b){
int i; // index valtozo
kiirIntegerTomb(tomb, meret); // tomb aktualis allapotanak kiiratasa
for (i=0; i<meret; i++){ // vegig iteralunk a tombunkon (0. elemtol a legutolsoig)
if (tomb[i] == a){ // ha a tomb i-edik (azaz az aktualis) eleme egyenlo a 3. parameter ertekevel
tomb[i] = b; // akkor kicsereljuk a 4. perameter ertekere
}
}
kiirIntegerTomb(tomb, meret); // tomb aktualis allapotnak kiiratasa, hogy lassuk a valtozasokat
}
/**
(d) A (c) feladat módosítása úgy, hogy a 3. paraméterben megadott indexszu elemet cseréljük ki, a 4. paraméterben megadott indexszu elemmel.
Tehát a 3. elemet a 4.-re, a 4.-et pedig a 3.-ra.
*/
void fgvD(int tomb[], int meret, int a, int b){
int i, tmp; // indexvaltozo es segedvaltozo a cserehez
kiirIntegerTomb(tomb, meret);
for (i=0; i<meret; i++){
if (i == a){ // ha az index megegyezik a 3. parameter ertekevel, akkor 3 lepeses ("haromszog") csere
tmp = tomb[a]; // kimentjuk az egyik elemet
tomb[a] = tomb[b]; // ekkor mar felul irhatjuk
tomb[b] = tmp; // a tmp-be mentett ertek mehet a masik valtozoba
}
}
kiirIntegerTomb(tomb, meret);
}
/**
(e) Hozzunk létre egy integer 2D tömböt és töltsük fel a szorzótábla értékeivel.
*/
int fgvE(int tomb[][10]) { // egy 2D tomb parameterkent valo megadasakor meg kell adni a meretet is legalabb a masodik tomb zarojelben
int i,j; // index valtozok
for (i=0;i<10;i++){ // az i fogja jelolni, hogy hanyadik sorban jarunk
for (j=0;j<10;j++){ // a j fogja jelolni, hogy hanyadik oszlopban jarunk
tomb[i][j]=(i+1)*(j+1); // a tomb [i][j]-edik (azaz az aktualis) elemet felul irjuk.
// i*j lenne normal korulmenyek kozott, viszont mivel 0-tol indul az indexszeles,
// ezert az i es a j erteket is szorzas elott megnoveljuk 1-gyel, hogy ne 0*0-val,
// hanem 1*1-gyel kezdjuk a tablat.
printf("%4d", tomb[i][j]); // kiiratas
}
printf("\n"); // ujsor irasa minden egyes sornak a feldolgozasa utan. Ez csak a tablazatos megjelenites miatt kell.
// a tomb feldolgozasan es feltoltesen nem valtoztat
}
return 0; // mivel nem volt megadva, hogy mi legyen a visszateresi ertek, ezert 0-t adunk vissza
}
/**
(f) Ellenorizzuk le egy karaktertomb hosszat. Toljuk el az osszes karaktert 1-gyel. A fuggveny a tomb meretevel terjen vissza.
*/
int fgvF(char tomb[]){
int i = 0, meret = 0; // indexvaltozo es a meret tarolasara hasznalt valtozo letrehozasa. Nagyon fontos a 0 kezdoertek!!
while (tomb[i] != 0){ // egesz addig haladunk vegig a tombon, amig meg nem latjuk a 0 jelet, ami a veget jelzi.
meret++; // noveljuk a meret valtozot, amig nem latjuk a 0 jelet
i++; // INDEXVALTOZO NOVELESE, HOGY NE LEGYEN VEGTELEN CIKLUS
}
kiirCharTomb(tomb);
for (i=0;i<meret;i++){ // vegig iteralunk a tombon. Most felhasznaljuk a meret valtozot, de itt is megtehetnenk, hogy 0 jelig megyunk
tomb[i] = tomb[i] + 1; // minden karakterhez adjunk hozza 1-et.
}
kiirCharTomb(tomb);
return meret; // visszateresi ertek
}
int main(){
int t1[10] = {5,3,12,7,4,89,1,8,9,45}, // 1D int tomb
t2[10][10], // 2D int tomb
m = 10; // meret valtozo
char t3[8] = {'b','e','l','a','f','u','t', 0}; // 1D char tomb
/** Pelda a fuggvenyek hivasara */
// fgvA(20);
// printf("%d\n",fgvB(t1,m));
// fgvC(t1,m,3,100);
// fgvD(t1,m,2,5);
// fgvE(t2);
// fgvF(t3);
return 0;
}
Plusz pontos házi feladat
Írjunk egy bombakereső játékot. A játékot a számítógép ellen játszuk. A számítógép induláskor feltölt egy 10*10-es tömböt random 0-kal és 1-esekkel, úgyh pl. 10 darab 1-es legyen a táblán, a többi mind 0. Az 1-esek szimbolizálják a bombákat. A játék kezdésekor a játékos úgyszint kitölt egy ugyanekkora táblát a saját elképzelése szerint, úgy, hogy megadja, hogy melyik pozícióra kerüljönek a bombái: [i,j]. Miután a random generátor feltöltötte az ellenfél tábláját, illetve a játékos is feltöltötte a sajátját, megkezdődhet a játék. Ezután felváltva találgathat a két játékos, hogy szerintük hol lehet bomba az ellenfél tábláján. Az nyer, aki előbb megtalálja az ellenfél összes bombáját. A játék végén írjuk ki, hogy ki nyerte a játszmát.
Beküldési határidő: 2017. 10. 09., éjfél
Küldés STUD-os email címről, melynek tárgya: [progalap2017][05][plusz], tartalma pedig a csatolt .c fájl.
Egy kis segítség: nyugodtan töltsük fel mindkét táblát kezdetben 0-kal, majd ahova érkezik 1-es, ott csak írjuk felül. A számítógép táblájának feltöltésére egy lehetséges megoldás, hogy két számot generáltatunk: az egyik lesz az oszlop indexe, a másik pedig a sornak az indexe. Így jön ki pl., hogy hova kerüljön 1-es. Bevezethetünk két segéd változót, amely azt figyeli, hogy ki hány bombát talált eddig. Ha valamelyik változó értéke eléri a 10-et, meg van az összes bomba, a játék véget ér.
Ügyeljünk oda, hogy: Kétszer ugyanoda tippelve, ne szerezzen egyik játékos sem mégegyszer pontot. Legyegyszerűbb, ha egy találat esetén egyből felülírjuk 0-val az adott pozíciót az adott tömbben. Ellenőrizzük, a játékos által megadott pozíciókra, hogy a 10*10-es tömbön belül vannak-e.
A kódot kommentezni kell, magyarul! (Melyik változó / elágazás / ciklus / függvény /.. mire való.)
Csak akkor jár plusz pont, ha az összes kritérium teljesül!
A feladat megoldását/próbálgatását erősen ajánlom mindenkinek. Sok mindenre rá lehet jönni ilyenkor. Próbáljunk meg a körülményekhez mérten felhasználóbarát programot készíteni!
Egy lehetséges megoldás: bomb.c
Jövő héten 3. miniZH (2017.10.09.)
6. gyakorlat
Rekurzió
Rekurziónak nevezzük, amikor egy függvény önmagát hívja, egy bizonyos feltétel teljesüléséig. Sokkal elegánsabb megoldást kapunk és csökkenti a redundanciát a kódunkban. Használata akkor ajánlott, ha egy bizonyos függvény hívását egymás után többször végre kell hajtani. Azonban a számítási idő és a memóriaigény jelentős növekedése miatt az esetek többségében mégis az iteratív megoldás ajánlott.
F: n faktoriális kiszámítása rekurzió NÉLKÜL
=============================================================================
#include <stdio.h>
int main(){
int n, i;
long f = 1;
printf("Enter an integer to find factorial: ");
scanf("%d", &n);
for (i=1; i<=n; i++){
f *= i;
}
printf("n! = %d\n", f);
return 0;
}
F: n faktoriális kiszámítása rekurzív módszerrel
=============================================================================
#include <stdio.h>
long factorial(int);
int main(){
int n;
long f;
printf("Enter an integer to find factorial\n");
scanf("%d", &n);
if (n < 0){
printf("Negative integers are not allowed.\n");
} else {
f = factorial(n);
printf("%d! = %ld\n", n, f);
}
return 0;
}
long factorial(int n){
if (n == 0)
return 1;
else
return(n * factorial(n-1));
}
/*
n = 5 esetén
5 * factorial(5-1) =
5 * 4 * factorial(4-1) =
5 * 4 * 3 * factorial(3-1) =
5 * 4 * 3 * 2 * factorial(2-1) =
5 * 4 * 3 * 2 * 1 * factorial(1-1) =
5 * 4 * 3 * 2 * 1 * 1 = 120
*/
Gyakorlás
1. Feladat: Írj programot, mely bekér egy n pozitív egész számot, és megadja az első n természetes szám összegét! Készíts kétféle algoritmust két külön függvényben, és mindkettővel számoltasd ki az eredményt:
- (a) Az első egy mechanikus, ciklust használó algoritmus legyen. (1+2+3+...+N)
- (b) A másik matematikailag átgondolt, minél egyszerűbb algoritmus legyen. ( bővebben ).
Hasonlítsd össze a két megoldás futásidejét! (időmérő futtatáshoz: time ./prog) Ehhez használj long long int típusú értékeket.
2. Feladat: Számoljuk ki egy véletlen mátrix sor/oszlop minimumát/maximumát.
// random számok megadásának módja
#include <time.h> // Program elején, a többi include mellett.
srand(time(NULL)); // A main első sora
rand(); // Sorsolás helyén. Ha le akarjuk korlátozni, akkor a rand() után %n-nel kell megadni a felső korlátot. Az alsó automatikusan 0 lesz. Pl. rand() % 2 -> [0,1]
3. Feladat: Írjunk programot, mely a bemeneti szöveg kiírását végzi el a képernyőre, számjegyek nélkül.
4. Feladat: Béla és egyik haverja moziba mennek. A mozitermen közepén végig vezet egy folyosó lefelé és a folyosó jobb és bal oldalán egyforma mennyiségű ülőhely található (tehát pl. 10 sor és 10 oszlop mindkét oldalon). Mindketten nagyon nagy fanok így a lehető legjobb pozícióból szeretnék látni a vásznat. Azaz mindketten a középső folyosó mellett szeretnének ülni közvetlenül, de ugyanabban a sorban. Készíts két darab N*N-es integer tömböt, majd töltsd fel őket random 0-kal és 1-esekkel. Írasd ki őket egymás mellé. Majd keresd meg azokat a sorokat, amelyeknél az 1. tömb utolsó oszlopában és a 2. tömb első oszlopában egymás mellett 0 található.
5. Feladat: FIFO (First In First Out) algoritmus megvalósítása 1D tömbbel. Töltsünk fel egy 10 méretű, 1D tömböt 1..10 elemekkel. Majd olvassunk be egy értéket egy változóba. Ha az az érték már szerepel a tömbben, akkor ne tegyünk semmit, mivel nincs szükség cserére. Ha viszont nincs benne az érték a tömbben, akkor töröljük a tömbből a legrégebben betöltött elemet és tegyük a tömb végére a beolvasott értéket. Ciklussal olvassunk be bizonyos mennyiségű elemet.
Megoldás letöltése: gyak06.c, illetve online verziója:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define N 4
#define M 5
#define S 10
void kiir (int t[]){
int i;
for (i=0; i<S; i++){
printf("%d ", t[i]);
}
printf("\n");
}
int fgv1A(int n){
int i, ossz = 0;
for (i=1; i<=n; i++){ // az elso n szamon fogunk vegig iteralni
ossz += i; // mindig hozza adjuk az aktualist az elozo osszeghez
}
return ossz; // osszeg visszaadasa (osszeggel valo visszateres)
}
int fgv1B(int n){
return n*(n+1)/2; // keplet eredmenyenek a visszaadasa
}
void fgv2(int t[][M]){ // jelenleg sorminimumok szamolasa
srand(time(NULL));
int i, j, min = 0;
for (i=0; i<N; i++){ // sorokat hatarozza meg
for (j=0; j<M; j++){ // oszlopokat hatarozza meg
t[i][j] = rand() % 100; // random sorsolunk: [0, 99]
}
}
for (i=0; i<N; i++){ // sorokat hatarozza meg
min = t[i][0]; // kimentjuk az adott sor legelso oszlopanak elemet es vele fogjuk kezdeni az osszehasonlitast, igy nem fordulhat elo, hogy tul kicsi kezdoerteket adunk meg
for (j=0; j<M; j++){ // oszlopokat hatarozza meg
printf("%3d", t[i][j]); // kiiratjuk a tomb elemeit
if (t[i][j] < min){ // ha a tomb adott eleme kisebb, mint a min
min = t[i][j]; // akkor, az azt jelenti, hogy "ő" a jelenlegi legkisebb elem, irjuk felul vele a min-t
}
}
printf(" | %2d\n", min); // sorminimumok kiirasa
}
}
void fgv3(char t[]){
int i;
printf("%s\n",t); // tomb kiiratasa
for (i=0; t[i] != 0; i++){
if (t[i] < '0' || t[i] > '9'){ // amennyiben az adott karakter nem szam (ASCII)
printf("%c", t[i]); // akkor kiiratjuk
}
}
printf("\n");
// NEM elfelejteni, hogy az elozo ciklus nem valtoztat a tomb tartalman,
// csupan a kiiratast vegezzuk el szamok nelkul
}
void fgv4(int t1[][S], int t2[][S]){
srand(time(NULL));
int i, j;
for (i=0; i<S; i++){ // sorokat hatarozza meg
for (j=0; j<S; j++){ // oszlopokat hatarozza meg
t1[i][j] = rand() % 2; // random sorsolunk: [0, 1]
t2[i][j] = rand() % 2; // random sorsolunk: [0, 1]
}
}
// a ket tomb kiiratasa egymas melle
for (i=0; i<S; i++){ // sorokat hatarozza meg
for (j=0; j<S; j++){ // oszlopokat hatarozza meg
printf("%2d", t1[i][j]);
}
printf("\t");
for (j=0; j<S; j++){ // oszlopokat hatarozza meg
printf("%2d", t2[i][j]);
}
printf("\n");
}
printf("\nAjanlott sorok:\n");
for (i=0; i<S; i++){
// ha a t1 utolso oszlopa es a t2 elso oszlopa is nulla, ugyanabban a sorban
if (t1[i][S-1] == 0 && t2[i][0] == 0){
printf("%d.\n", i+1); // akkor kiiratjuk (azert i+1, hogy ne 0-tol indexszeljunk)
}
}
}
void fgv5 (int t[], int bejovo){
int i, voltE = 0;
for (i=0;i<S;i++){ // vegig elemezzuk a tombot
if (t[i] == bejovo){ // ha a bejovo ertek szerepel benne
voltE = 1; // akkor egy valtozo erteket 1-re allitjuk
}
}
if (voltE == 0){ // amennyiben a valtozo erteke 0, tehat meg nincs a tombben az ertek
for (i=0;i<S;i++){
t[i] = t[i+1]; // akkor az osszes elemet eggyel balra mozgatjuk
}
t[S-1] = bejovo; // es az utolso poziciora behelyezzuk az uj erteket
// igy mindig a tomb elso eleme kerult bele a legregebben / legeloszor
}
}
int main(){
int be, i,
tInt1[N][M], // NxM 2D tomb
tInt21[S][S], tInt22[S][S], // 2 db SxS 2D tomb
tInt3[S] = {1,2,3,4,5,6,7,8,9,10}; // 1D tomb
char tChar[S] = {'B','2','8','e','2','l','9','a','6',0}; // 1D karakter tomb
/** Pelda a fuggvenyek hivasara */
// printf("%d\n",fgv1A(5));
// printf("%d\n",fgv1B(5));
// fgv2(tInt1);
// fgv3(tChar);
// fgv4(tInt21, tInt22);
/*
for (i=0; i<5; i++){
kiir(tInt3);
printf("Adj meg egy erteket: ");
scanf("%d",&be);
fgv5(tInt3, be);
kiir(tInt3);
putchar('\n');
}
*/
return 0;
}
7. gyakorlat
Input/Output műveletek FILE-ból/ba
F: Írj egy programot, ami beolvas két egész számot, majd kiírja az összegüket
és a szorzatukat.
==============================================================================
#include <stdio.h>
int main() {
int a, b;
scanf("%d %d", &a, &b);
printf("Osszeg: %d\nSzorzat: %d\n", a + b, a * b);
return 0;
}
F: Módosítsuk úgy a programot, hogy használja az stdin, stdout, fscanf és
fprintf függvényeket.
==============================================================================
#include <stdio.h>
int main() {
int a, b;
fscanf(stdin, "%d %d", &a, &b);
fprintf(stdout, "Osszeg: %d\nSzorzat: %d\n", a + b, a * b);
return 0;
}
/*
látható, hogyha elhagynánk az fscanf és az fprintf elejéről az f betűket,
valamint a zárójelekből az stdin és stdout függvényeket,
az előző feladattal ekvivalens megvalósítást kapnánk
*/
A fájlok adatfolyamként történő kezelése során egy FILE * típusú ún. filemutató azonosítja az állományt. (Ezt az állományt az STDIO.H file deklarálja.)
FILE *nev;
Ahhoz, hogy a háttértáron levő file tartalmához hozzáférjünk, a file-t meg kell nyitnunk. A file megnyitását a fopen függvény hívásával végezhetjük el, melynek prototípusa:
FILE * fopen(const char * filename, const char *mode);
avagy meglévő FILE * esetén
FILE * vmi;
vmi = fopen(const char * filename, const char *mode);
A filename helyére a beolvasandó file neve kerül, tehát egy sztring. (pl.: "be.txt")
A mode úgyszint egy sztring, amely a file elérését és típusát határozza meg. (pl.: "r")
A lehetséges elérési módok:
"r" - Létező file megnyitása olvasásra. "w" - Új file megnyitása írásra. Ha file már létezik, akkor a tartalma elvész. "a" - File megnyitása hozzáírásra. A nyitás után a file végén lesz az aktuális file-pozíció. Ha a file nem létezik, akkor az fopen létrehozza azt. "r+" - Létező file megnyitása írásra és olvasásra (update). "w+" - Új file megnyitása írásra és olvasásra (update). Ha a file már létezik, akkor a tartalma elvész. "a+" - File megnyitása a file végén végzett írásra és olvasásra (update). Ha a file nem létezik, akkor az fopen létrehozza azt.
Amikor többé nincs szükségünk a megnyitott file(ok)-ra, akkor kell használnunk az fclose hívást, amely lezárja a file-t.
fclose(vmi);
F: Módosítsuk úgy az előző programot, hogy valódi fájlokat használjon.
==============================================================================
#include <stdio.h>
int main() {
int a, b;
FILE *infile; // beolvasáshoz filemutató
FILE *outfile; // kiíratáshoz filemutató
infile = fopen("be.txt", "r"); // bementi fájl olvasásra
outfile = fopen("ki.txt", "w"); // kimeneti fájl írásra
fscanf(infile, "%d %d", &a, &b); // a megadott bementi fájlból (be.txt) beolvasunk 2 egész számot
fprintf(outfile, "Osszeg: %d\nSzorzat: %d\n", a + b, a * b); // majd a megadott kimeneti fájlba (ki.txt) kiírjuk a beolvasott 2 egész számot
fclose(infile); // bemeneti fájl lezárása
fclose(outfile); // kimeneti fájl lezárása
return 0;
}
be.txt :
3 4
ki.txt :
Osszeg: 7 Szorzat: 12
A be.txt-nek léteznie kell, viszont a ki.txt-t a program létrehozza magától, amennyiben nem volt ellőállítva.
Ha a megadott állományt nem sikerült megnyitni, vagy ha a FILE struktúrának nem sikerült helyet foglalni a memóriában, NULL lesz a függvényérték.
F: Hibakóddal lépjen ki a program, ha valamelyik fájl megnyitása nem sikerült.
==============================================================================
#include <stdio.h>
int main() {
int a, b;
FILE *infile;
FILE *outfile;
/*
ha nem sikerült megnyitni a fájlt, akkor NULL-t kapunk, ami HAMIS értéknek számít,
!NULL ebből adódóan IGAZ érték lesz,
tehát teljesül az IF feltétele és 1-es értékkel tér vissza a program
*/
if(!(infile = fopen("be.txt", "r"))) {
return 1;
}
if(!(outfile = fopen("ki.txt", "w"))) {
fclose(infile);
return 1;
}
fscanf(infile, "%d %d", &a, &b);
fprintf(outfile, "Osszeg: %d\nSzorzat: %d\n", a + b, a * b);
fclose(infile);
fclose(outfile);
return 0;
}
1. Feladat: Írj egy programot, ami bekéri egy torta piskótájanak sugarát és magasságát, majd kiszámolja a torta térfogatá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. A program a "be.txt" nevű fájlból olvassa be az adatokat és a "ki.txt" nevű fájlba írja ki az eredményt.
Egy lehetséges megoldás letöltése: fileIO_henger.c, illetve online verziója:
#include<stdio.h>
#include<math.h> // Matematikai konyvtar: innen jon a pi: M_PI (3.14159265358979323846)
void torta(){
float r, h; // sugar es magassag segedvaltozok letrehozasa
// Fajlok letrehozasa
FILE *be, *ki;
// fajlok megnyitasa
be = fopen("be.txt", "r");
ki = fopen("ki.txt", "w");
fscanf(be, "%f %f", &r, &h); // bemetrol valo olvasa
fprintf(ki, "%f\n", r*r*M_PI*h); // kimenetre valo iras: Henger terfogat keplete.
// fajlok lezarasa
fclose(be);
fclose(ki);
}
int main(){
/** Fuggveny hivasara pelda **/
torta();
return 0;
}
Sorozatok
1. Feladat: Egy számtani sorozat első tagja A, differenciája D. Számítsa ki a sorozat N-edik tagját!
Egy sorozatot akkor nevezünk számtani sorozatnak, ha a szomszédos elemek különbsége – differenciája – (a sorozatra jellemző) állandó. Pl. (a1=1, d=1) 1,2,3,4,..., vagy (a1=12, d=15) 12,27,42,57,72... Részletesen lásd: Wikipedia
2. Feladat: Egy mértani sorozat első tagja A, hányadosa Q. Számítsa ki a sorozat N-dik tagját!
Egy sorozatot akkor nevezünk mértani sorozatnak, ha (a másodiktól kezdve) bármelyik tag és az azt megelőző tag hányadosa állandó. Ezt a hányadost idegen szóval kvóciensnek nevezzük. Jele: q. Pl. (a1=3, q=3) 3, 9, 27, 81, ... Részletesen lásd: Wikipedia
Egy lehetséges megoldás letöltése: sorozatok.c, illetve online verziója:
#include<stdio.h>
/** 1. Feladat **/
float szamtani(float A, float D, int N){
int i;
for (i=2; i<=N; i++){
A = A + D;
printf("(%d) %f ", i, A);
}
return A;
}
/** 2. feladat **/
float mertani(float A, float Q, int N){
int i;
for (i=2; i<=N; i++){
A = A*Q;
printf("(%d) %f ", i, A);
}
return A;
}
int main(){
/** Pelda a fuggvenyek hivasara **/
printf(" Szamtani sorozat N-ig: \n");
szamtani(1.5, 3, 4);
printf("\n\n Mertani sorozat N-ig: \n");
mertani(3, 3, 5);
return 0;
}
További gyakorló feladatok
1. Feladat: Általánosítsd a Fibonacci sorozatot: tekintsük azt a k-ad rendű Fibonacci sorozatot, melynek első k eleme 1 (azaz a[0]=..a[k-1]=1), a többi elemét pedig az előző k elem összegeként kapjuk, (azaz a[n]=a[n-1]+...+a[n-k]). Írj programot, mely bekér egy k és egy n természetes számot, és kiszámítja a k-ad rendű Fibonacci-sorozat n-ik elemét!
2. Feladat: Valósítsuk meg a beszúró rendezés algoritmust! Részletesen lásd: Wikipedia
Egy lehetséges megoldás letöltése: gyak07.c, illetve online verziója:
#include<stdio.h>
#define M1 20
#define M2 10
void kiir(int t[], int n){
int i;
for (i=0; i<n; i++){
printf("%d ", t[i]);
}
printf("\n");
}
/** 1. feladat **/
void fiboK(int t[], int k, int n){
int i, j;
for (i=0; i<k; i++){ // az elso k elemet feltoltjuk 1-esekkel
t[i] = 1;
}
for (i=k; i<n; i++){ // n-edik elem szamolasa: i=k..n
t[i] = 0; // kinulazzuk az aktulis elemet, hogy ide tudjuk osszegezni a megelozo k elemet
for (j=i-k; j<i; j++){ // visszalepunk k elemnyit (i-k), es az aktualis elemig (i) osszegzunk
t[i] += t[j];
}
}
kiir(t, n);
}
/** 2. feladat **/
void beszuroRendezes(int t[], int n){
int count = 0, // itt gyujtjuk majd, hogy a vizsgalt elem hany tombbeli elemnel nagyobb
i, j, // indexvaltozok
tmp; // ide mentjuk majd ki a vizsgalt elemet
kiir(t, n);
for (i=1; i<n; i++){ // az osszes elemet meg kell vizsgalni egyszer (kiveve az elsot, mivel neki ugysincs balrol szomszedja)
count = 0; // minden elem eseten nullazas, hogy ujra gyujthessuk az aktualis osszeget
tmp = t[i]; // mentsuk ki a vizsgalt elemet, hogy a tomb eleminek mozgatasakor ne vesszen el
for (j=0; j<i; j++){ // menjunk vegig az egesz tombon es vizsgaljuk meg, hogy az aktualis elem hany elemnel nagyobb
if (tmp > t[j]){
count++; // ha nagyobb, akkor noveljuk a fenntartott valtozo erteket
}
}
for (j=i; j>count; j--){ // ha balrol jobbra akarunk elemeket mozgatni, akkor jobbrol balra kell feldolgozni oket
t[j] = t[j-1]; // jobbra mozgatas
}
t[count] = tmp; // ahany elem nagyobb volt a vizsgalt elemnel, arra a poziciora kell tenni a muvelet vegen
kiir(t, n);
//getchar(); // komment kitorlesevel elemenkenti mozgatasonkent Enter utesevel engedhetjuk tovabb a program futasat
}
kiir(t, n);
}
int main(){
int t1[M1], t2[M2] = {41,4,1,38,23,-12,78,12,-1000,2};
/** Fuggvenyek hivasara pelda **/
// fiboK(t1, 4, M1);
// output: 1 1 1 1 4 7 13 25 49 94 181 349 673 1297 2500 4819 9289 17905 34513 66526
beszuroRendezes(t2, M2);
// output: -1000 -12 1 2 4 12 23 38 41 78
return 0;
}
(!) JÖVŐ HÉTEN (2017.10.23.) A GYAKORLAT ELMARAD! 1956-os Forradalom Ünnepe (hosszú hétvége).
8. gyakorlat
A GYAKORLAT ELMARAD! 1956-os Forradalom Ünnepe (hosszú hétvége)
9. gyakorlat
Pointerek
Új dinamikus változó létesítése
p=malloc(sizeof(E));
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
*p
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
free(p);
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
p = &i;
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. Ez a referencia szerinti értékátadás.
Feladat: Futtasd le a cim.c programot, és értelmezd! Melyik érték melyik értékkel egyenlő, és miért?
/* $Id: cim.c 3234 2017-09-21 12:25:20Z gertom $ */
#include <stdio.h>
int main(){
int a = 10;
int *pa;
pa = &a;
printf("a=%d pa=%#x\n", a, (int)pa);
printf("&a=%#x &pa=%#x\n", (int)&a, (int)&pa);
printf("*pa=%d\n", *pa);
return 0;
}
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.
/* $Id: csere.c 3234 2017-09-21 12:25:20Z gertom $ */
#include <stdio.h>
void csere(int x, int y){
int tmp;
tmp = x;
x = y;
y = tmp;
}
int main(){
int x = 3, y = 5;
printf("A függvény előtt: x = %d, y = %d\n", x, y);
csere(x, y);
printf("A függvény után: x = %d, y = %d\n", x, y);
return 0;
}
Ugyebár ebben a formában a program nem hajtja végre azt, amit szeretnénk, mivel a main-ben illetve a csere függvényben találhato x és y változók teljesen különböző 2-2 változót jelölnek.
Megoldás - a változóknak nem az értékét, hanem a memória címét kell átadni, majd a csere függvényben pointereket használva, ezekre a címekre írni be a változtatásokat:
#include <stdio.h>
void csere(int *x, int *y){ // az x es y pointert a main-ben letrehozott x es y valtozok cimere allitjuk ezzel
int tmp;
// * segitsegevel a cimen levo erteket erjuk el, majd erjuk felul
tmp = *x;
*x = *y;
*y = tmp;
}
int main(){
int x = 3, y = 5;
printf("A függvény előtt: x = %d, y = %d\n", x, y);
csere(&x, &y); // x es y cimenek atadasa
printf("A függvény után: x = %d, y = %d\n", x, y);
return 0;
}
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?
#include <stdio.h>
#include <stdlib.h>
int main()
{
/* Két pointer deklarációja */
int *p, *q;
/* Két int típusú dinamikus változó létrehozása (memóriafoglalás) */
p = malloc(sizeof(int));
q = malloc(sizeof(int));
/* Értékadás a két dinamikus változónak */
*p = 3;
*q = 3;
printf("p és q %s\n", p == q ? "megegyezik" : "nem egyezik meg"); // NEM, mivel különböző mem. címekről van szó
printf("*p == %d, *q == %d\n", *p, *q);
*p = 4; // p által mutatott mem. címen tárolt érték felülírása
printf("*p == %d, *q == %d\n", *p, *q);
free(p); // p által mutatott mem. címen foglalt terület felszabadítása
p = q; // p is mutasson ezentúl oda, ahova a q
printf("p és q %s\n", p == q ? "megegyezik" : "nem egyezik meg"); // IGEN, mivel mostmár ugyannarról a mem. címről beszélünk
printf("*p == %d, *q == %d\n", *p, *q);
*p = 4; // mivel p és q ugyanoda mutat, ezért ha *p-t felül írjuk, akkor *q is ugyanaz lesz
printf("*p == %d, *q == %d\n", *p, *q);
free(q); // q által mutatott mem. címen foglalt terület felszabadítása
return 0;
}
Feladat: Módosítsuk a következő kódot úgy, hogy "értelmes" műveleteket végezzen.
#include <stdlib.h>
int main() {
int *p, *q;
p = malloc(sizeof(int));
free(q);
return 0;
}
Lehetséges módosítások: Írjuk át a free(q) utasításon belül a q változót p-re. Vagy a p mellé allokáljunk a q-nak is memória területet és a végén mind a két pointerhez tartozó memória területet szabadítsuk fel.
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.
#include <stdio.h>
int main(){
int t[10], i;
for (i=0; i<10; i++){
scanf("%d", &t[i]);
}
for (i=0; i<10; i++){
printf("%d ", t[i]);
}
printf("\n");
return 0;
}
Feladat: Az előző programban deklarálj egy int pointert is, és a beolvasást ennek segítségével valósítsd meg!
#include <stdio.h>
int main(){
int t[10], i, *p; // pointer letrehozasa
for (i=0; i<10; i++){
p = &t[i]; // pointer raallitasa a tomb aktualis elemere
scanf("%d", p); // a pointer altal mutatott teruletre olvasunk be, azaz a tomb i-edik elemebe
}
for (i=0; i<10; i++){
printf("%d ", t[i]);
}
printf("\n");
return 0;
}
Feladat: 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)!
#include <stdio.h>
#include <stdlib.h> // malloc miatt kell
int main(){
int *t, // pointer letrehozasa
i, meret;
printf("Add meg, hogy mekkora meretu dinamikus tombot szeretnel letrehozni: ");
scanf("%d", &meret);
t = malloc(meret*sizeof(int)); // allokaljunk meret mennyisegu integer nagysagu memoria teruletet, melyre a t fog mutatni
printf("Olvasd be a tomb elemeinek ertekeit:\n");
for (i=0; i<meret; i++){
scanf("%d", &t[i]);
//scanf("%d", t+i); // ekvivalens az elozo sorral
}
for (i=0; i<meret; i++){
printf("%d ", t[i]);
//printf("%d ", *(t+i)); // ekvivalens az elozo sorral
}
printf("\n");
return 0;
}
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?
/* $Id: tombbejaras.c 3234 2017-09-21 12:25:20Z gertom $ */
#include <stdio.h>
#define N 42
#define M 7
#define K 294 // 42*7
int main(){
int tomb[N][M];
int i = 0, j = 0, *p = NULL, *t = NULL; // a p és a t is ponterek, egyelore nem mutatnak sehova (ezert NULL)
p = t = (int*)tomb; // a p es a t is a tomb-re (a tomb kezdocimere) fognak mutatni
while (p < t + K) { // addig megy a ciklus, amig a p az utolso elem cimet meg nem haladja
*(p++) = i++; // a p altal mutatott teruletre beirjuk az i aktualis erteket, majd mind a ketto erteket noveljuk eggyel
}
for (i = 0; i < N; ++i) {
for (j = 0; j < M; ++j) {
printf(" %5d", tomb[i][j]);
}
putchar('\n');
}
return 0;
}
Plusz pontos órai 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:
Add meg, hogy milyen magas legyen a piramis: 5 Add meg milyen karakterbol epuljon fel a piramis: A A AAA AAAAA AAAAAAA AAAAAAAAA
Egy lehetséges megoldás: piramis.c
10. gyakorlat
Típus definiálás
A típus definiálás általános alakja:
typedef típus név
Feladat: Hogyan tudunk létrehozni egy olyan vector nevű tömb típust, amely egy háromdimenziós térbeli vektort reprezentál?
typedef double vector[3];
Feladat: Hogyan lehet létrehozni egy N hosszúságú sztringek tárolására szolgáló karaktertömb típust?
typedef char string[N+1];
Feladat: Hozz létre külön típust 16 bites nemnegatív értékek tárolására.
typedef unsigned short int u16;
Struktúrák
C nyelven a struktúra (struct) típus több tetszőleges típusú objektum együttese (kivéve a void és a függvény típust). Ezek az objektumok önálló, a struktúrán belül érvényes nevekkel rendelkeznek.
A struktúra szerkezetét meghatározó deklaráció általános formája:
struct struktúra_azonosító { típus1 tag1; típus2 tag2; ... típusN tagN; };
A fenti típussal változót az alábbi módon készíthetünk:
struct struktúra_azonosító struktúra_változó;
Az attributumokra a pont operátorral tudunk hivatkozni:
struktúra_változó.tag1
Példa: Könyvtárkezelő program készítése során jól alkalmazható az alábbi adatstruktúra:
struct book { char szerzo[20]; char cim[40]; int ev; int ar; };
Változók létrehozása:
struct book macska, gyerek, cprog;
Attribútumokra való hivatkozás:
macska.ev = 1992;
Elég gyakran alkalmazott megoldás a typedef kulcsszóra épül:
typedef struct book { char szerzo[20]; char cim[40]; int ev; int ar; } BOOK;
Változók létrehozása:
BOOK macska, gyerek, cprog;
Egyszerűbb példa egy személy megvalósítására:
// !
#include <stdio.h>
// ember struktúra
struct ember {
char nev[20];
int igszam;
int kor;
};
// kiíratás
void szemelykiiras(struct ember szemely){
printf(" Nev: %s\n", szemely.nev);
printf("Ig.szam: %d\n", szemely.igszam);
printf(" Kor: %d\n", szemely.kor);
}
int main (){
// személy létrehozása
struct ember BelaVagyok;
// szémélyhez tartozó attribútumok feltöltése
scanf("%s",&BelaVagyok.nev);
scanf("%d",&BelaVagyok.igszam);
scanf("%d",&BelaVagyok.kor);
// személy adatainak kiírása fgv segítségével
szemelykiiras(BelaVagyok);
return 0;
}
Ugyanez typedef-el:
// !
#include <stdio.h>
// ember struktúra
typedef struct {
char nev[20];
int igszam;
int kor;
} ember;
// kiíratás
void szemelykiiras(ember szemely){
printf(" Nev: %s\n", szemely.nev);
printf("Ig.szam: %d\n", szemely.igszam);
printf(" Kor: %d\n", szemely.kor);
}
int main (){
// személy létrehozása
ember BelaVagyok;
// szémélyhez tartozó attribútumok feltöltése
scanf("%s",&BelaVagyok.nev);
scanf("%d",&BelaVagyok.igszam);
scanf("%d",&BelaVagyok.kor);
// személy adatainak kiírása fgv segítségével
szemelykiiras(BelaVagyok);
return 0;
}
3 komolyabb példa (otthoni gyakorlásra):
F: Adott a síkon 3 pont, mi az általuk meghatározott háromszög területe?
==============================================================================
#include <stdio.h>
#include <math.h>
struct pont {
float x;
float y;
};
float tav(struct pont P, struct pont Q) {
return sqrtf((P.x-Q.x)*(P.x-Q.x) + (P.y-Q.y)*(P.y-Q.y));
}
int main() {
struct pont A, B, C;
float a,b,c,s;
scanf("%f %f", &A.x, &A.y);
scanf("%f %f", &B.x, &B.y);
scanf("%f %f", &C.x, &C.y);
a=tav(B, C);
b=tav(A, C);
c=tav(A, B);
s=(a+b+c)/2;
printf("Terulet: %f\n", sqrtf(s*(s-a)*(s-b)*(s-c)));
}
F: Készítsünk komplex számok tárolására alkalmas adatszerkezetet (egész
komponensekkel). Készítsünk továbbá olyan függvényeket, melyek feladata:
- kiír egy komplex számot az stdout-ra,
- összead két komplex számot, és visszaadja az eredményt
- összeszoroz két komplex számot, és visszaadja az eredményt
==============================================================================
== BEGIN komplex.c ===========================================================
#include <stdio.h>
typedef struct komplex {
int real;
int imag;
} komplex;
komplex add(komplex k1, komplex k2)
{
komplex e;
e.real = k1.real+k2.real;
e.imag = k1.imag+k2.imag;
return e;
}
komplex mul(komplex k1, komplex k2)
{
komplex e;
e.real = k1.real*k2.real-k1.imag*k2.imag;
e.imag = k1.imag*k2.real+k1.real*k2.imag;
return e;
}
void printk(komplex k)
{
printf("(%d%+di)\n", k.real, k.imag);
}
int main()
{
komplex x1,x2,e;
x1.real = 10;
x1.imag = 2;
x2.real = 20;
x2.imag = -3;
printk(x1);
printk(x2);
e = add(x1,x2);
printk(e);
printk(mul(x1,x2));
return 0;
}
F: Láncolt lista. Olvassunk be egész számokat egy láncolt listába egy adott
végjelig, majd írassuk ki őket.
==============================================================================
== BEGIN linkedlist.c ========================================================
#include <stdio.h>
#include <stdlib.h>
#define VEGJEL 0
struct cella {
int ertek;
struct cella *kov;
};
int main()
{
struct cella *elso = NULL;
struct cella *p;
int i;
scanf("%d", &i);
while(i!=VEGJEL) {
p = (struct cella*)malloc(sizeof(struct cella));
p->ertek = i;
p->kov = elso;
elso = p;
scanf("%d", &i);
}
for(p=elso; p!=NULL; p=p->kov) {
printf("%d\n", p->ertek);
}
while(elso!=NULL) {
p =elso;
elso=p->kov;
free(p);
}
return 0;
}
Únió
Ismerkedjünk meg a union típussal. Igazából nincs sok dolgunk, mivel a struct típussal kapcsolatban ismertetett formai megoldások a union típusra is alkalmazhatóak.
Egyetlen és egyben lényegi különbség az adattagok elhelyezkedése között van. Míg a struktúra adattagjai a memóriában egymás után helyezkednek el, addig az únió adattagjai közös címen kezdődnek (átlapoltak).
short a; int b; long long c; Memóriafoglalás: STRUCT UNION _______ _ | | | | | | long long c | | | _______ _ |_______| _| | | | | | | int b |_______| _ | long long c |_______| _| |_______| _ | int b | |_______| _| short a |_______| _| short a _| _|
A union szerkezetét meghatározó deklaráció általános formája:
union union_azonosító { típus1 tag1; típus2 tag2; ... típusN tagN; };
A fenti típussal változót az alábbi módon készíthetünk:
union union_azonosító union_változó;
F: Mi a különbség a struct és a union között? Deklarálj egy struct és egy
union típust ugyanolyan mezőkkel. Adj értéket a mezőknek, majd írasd ki
őket!
==============================================================================
#include <stdio.h>
typedef struct {int i; double d; char c; float f;} st;
typedef union {int i; double d; char c; float f;} un;
int main()
{
st s;
un u;
s.i = u.i = 12345;
printf("s.i: %d u.i: %d\n", s.i, u.i);
s.d = u.d = 3.141593;
printf("s.d: %lf u.d: %lf\n", s.d, u.d);
s.c = u.c = 'A';
printf("s.c: %c u.c: %c\n", s.c, u.c);
s.f = u.f = 2.718281;
printf("s.f: %f u.f: %f\n", s.f, u.f);
return 0;
}
// !
Így nem látszik semmi. De mi van, ha egyszerre íratjuk ki a mezők értékeit?
==============================================================================
#include <stdio.h>
typedef struct {int i; double d; char c; float f;} st;
typedef union {int i; double d; char c; float f;} un;
int main()
{
st s;
un u;
s.i = u.i = 12345;
s.d = u.d = 3.141593;
s.c = u.c = 'A';
s.f = u.f = 2.718281;
printf("s.i: %d u.i: %d\n", s.i, u.i);
printf("s.d: %lf u.d: %lf\n", s.d, u.d);
printf("s.c: %c u.c: %c\n", s.c, u.c);
printf("s.f: %f u.f: %f\n", s.f, u.f);
return 0;
}
// !
F: Írasd ki a mezők kezdőcímét!
==============================================================================
== BEGIN union.c =============================================================
#include <stdio.h>
typedef struct {int i; double d; char c; float f;} st;
typedef union {int i; double d; char c; float f;} un;
int main()
{
st s;
un u;
printf("s.i: %p u.i: %p\n", &s.i, &u.i);
printf("s.d: %p u.d: %p\n", &s.d, &u.d);
printf("s.c: %p u.c: %p\n", &s.c, &u.c);
printf("s.f: %p u.f: %p\n", &s.f, &u.f);
return 0;
}
Feladat: Írjunk egy programot, melyen belül megvalósítjuk az 1D mátrixszorzást. A mátrixokat és méretüket egy struktúrán belül tároljuk le. A mátrixoknak dinamikusan foglaljunk helyet. Minden egyes műveletet külön függvényben programozzunk le. Használjunk pointereket a függvény paramétereinek átadásakor. Ne feledkezzünk meg a lefoglalt memória területek felszabadításáról sem.
Egy lehetséges megoldás: matrixszorzas1D.c
/*
Pelda 1D matrixszorzasra:
m1 = [1,2,3]
m2' = [4,5,6]
result = m1 * m2 = 1*4 + 2*5 + 3*6
*/
#include <stdio.h>
#include <stdlib.h>
typedef double* data_t; // double tipusu mutato tipus letrehozasa
typedef struct { // matrix_t nevu struktura definialasa, melynek ket attributuma van: n es data
int n;
data_t data;
} matrix_t;
/*
Struktura elemeinek feltoltese
*/
void beolvas (matrix_t *mat){
int i;
printf("Add meg a meretet: ");
/*
KET FELE MODSZERT IS MUTATUNK A STRUKTURAK HASZNALATARA,
MELYEK KOZUL BARMELY MEGKOZELITEST HASZNALHATJUK (A TOBBI FUGGVENYBEN IS)
*/
scanf("%d", &(*mat).n);
//scanf("%d", &mat->n); // Ekvivalens az elozo sorral
(*mat).data = malloc( (*mat).n * sizeof(double)); // helyet foglalunk a memoriaban matrix elemeinek
//mat->data = malloc( mat->n * sizeof(double)); // Ekvivalens az elozo sorral
for (i=0; i < mat->n ; i++){ // feltoltjuk a matrix elemit
scanf("%lf", &(*mat).data[i]);
//scanf("%lf", &mat->data[i]); // Ekvivalens az elozo sorral
}
}
/*
Struktura elemeinek kiiratasa
*/
void kiir (matrix_t *mat){
int i=0;
printf("\n%d\n", mat->n);
for (i=0; i < mat->n ; i++){
printf("%lf ", mat->data[i]);
}
}
/*
Strukturak matrix attributumainak osszeszorzasa
*/
int szoroz (matrix_t *mat_1, matrix_t *mat_2, double *res){
int i;
if (mat_1->n != mat_2->n){ // leellenorizzuk, hogy egyforma meretuek-e a matrixok
return 0; // ha nem, akkor 0 (hamis) ertekkel terunk vissza
} else {
for (i=0; i< mat_1->n ; i++){ // vegig nezzuk az osszes elemet
*res += mat_1->data[i] * mat_2->data[i]; // es feltoltjuk a szorzatokkal
}
return 1;
}
}
/*
Strukturan belul foglalt memoriaterulet felszabaditasa
*/
void torol(matrix_t *mat){
free(mat->data);
}
int main(){
int sikeres;
double eredmeny = 0;
matrix_t elso, masodik; // 2 matrix_t struktura letrehozasa
beolvas(&elso); // ertekadas
beolvas(&masodik); // ertekadas
// kiir(&elso);
// kiir(&masodik);
sikeres = szoroz(&elso, &masodik, &eredmeny); // ket matrix osszeszorzasa. A sikeres volt a szorzas a "sikeres" valtozo erteke 1 lesz, ellenkezo esetben 0
if (sikeres == 1){
printf("Az eredmeny: %lf\n", eredmeny);
} else {
printf("\nA mereteknek meg kell egyezniuk!\n");
}
torol(&elso); // lefoglalt memoriaterulet felszabaditasa
torol(&masodik); // lefoglalt memoriaterulet felszabaditasa
return 0;
}
Feladat otthonra: Alakítsuk át az előző feladatot úgy, hogy 2D mátrixokra is működjön. Figyeljünk arra, hogy két mátrix csak akkor szorozható össze, ha az első mátrixnak a szélessége, megegyezik a második mátrix magasságával.
Egy lehetséges megoldás: matrixszorzas2D.c
/*
Pelda 2D matrixszorzasra:
m1 = |1 2|
|3 4|
m2 = |4 5 6|
|7 8 9|
result = |18 21 24|
|40 47 54|
*/
#include <stdio.h>
#include <stdlib.h>
typedef double** data_t; // double tipusu mutato tipus letrehozasa
typedef struct { // matrix_t nevu struktura definialasa, melynek ket attributuma van: n es data
int n, m;
data_t data;
} matrix_t;
/*
Struktura elemeinek feltoltese
*/
void beolvas (matrix_t *mat){
int i, j;
printf("Add meg a magassagot: ");
scanf("%d", &mat->n);
printf("Add meg a szelesseget: ");
scanf("%d", &mat->m);
mat->data = malloc( mat->n * sizeof(double*)); // helyet foglalunk a memoriaban matrix elemeinek
for (i=0; i < mat->n ; i++){
mat->data[i] = malloc( mat->m * sizeof(double));
}
for (i=0; i < mat->n ; i++){ // feltoltjuk a matrix elemit
for (j=0; j < mat->m ; j++){
scanf("%lf", &mat->data[i][j]);
}
}
}
/*
Struktura elemeinek kiiratasa
*/
void kiir (matrix_t *mat){
int i, j;
printf("\nMagassag = %d, Szelesseg = %d\n", mat->n, mat->m);
for (i=0; i < mat->n ; i++){
for (j=0; j < mat->m ; j++){
printf("%2.2lf ", mat->data[i][j]);
}
printf("\n");
}
}
/*
Strukturak matrix attributumainak osszeszorzasa
*/
int szoroz (matrix_t *mat_1, matrix_t *mat_2, matrix_t *mat_res){
int i, j, k;
double osszeg = 0;
if (mat_1->m != mat_2->n){ // leellenorizzuk, hogy osszeszorozhatoak-e
return 0; // ha nem, akkor 0 (hamis) ertekkel terunk vissza
} else {
/* Eredmeny struktura elemeinek feltoltese */
mat_res->n = mat_1->n; // a magassaga az elso matrix magassaga lesz
mat_res->m = mat_2->m; // a szelessege az masodik matrix szelessege lesz
// 2D matrix memoria foglalasa
mat_res->data = malloc(mat_res->n * sizeof(double*)); // N darab
for (i=0; i < mat_res->n; i++){ // majd mindhez M darab
mat_res->data[i] = malloc(mat_res->m * sizeof(double));
}
// Ezen elmelkedjunk el egy kicsit, hogy hogy is jon ki ez a 3 egymasba agyazott for ciklus (rajzoljuk is le, ha szukseges)
for (k = 0; k < mat_res->m ; k++){
for (i = 0 ; i < mat_1->n ; i++){
for (j = 0; j< mat_2->n ; j++){
osszeg += mat_1->data[i][j] * mat_2->data[j][k];
}
mat_res->data[i][k] = osszeg;
osszeg = 0;
}
}
return 1;
}
}
/*
Strukturan belul foglalt memoriaterulet felszabaditasa
*/
void torol(matrix_t *mat){
int i;
for (i=0; i < mat->n ; i++){
free(mat->data[i]);
}
free(mat->data);
}
int main(){
int sikeres;
matrix_t elso, masodik, eredmeny; // 2 matrix_t struktura letrehozasa
beolvas(&elso); // ertekadas
beolvas(&masodik); // ertekadas
kiir(&elso);
kiir(&masodik);
sikeres = szoroz(&elso, &masodik, &eredmeny); // ket matrix osszeszorzasa. A sikeres volt a szorzas a "sikeres" valtozo erteke 1 lesz, ellenkezo esetben 0
if (sikeres == 1){
kiir(&eredmeny);
torol(&eredmeny);
} else {
printf("\nA mereteknek meg kell egyezniuk!\n");
}
torol(&elso); // lefoglalt memoriaterulet felszabaditasa
torol(&masodik); // lefoglalt memoriaterulet felszabaditasa
return 0;
}
Plusz pontos házi feladat:
Leírás elérhető .pdf formátumban: akasztofa.pdf
Küldés STUD-os email címről, melynek tárgya: [progalap2017][10][plusz], tartalma pedig maga a kód, vagy a csatolt .c fájl.
A feladat megoldásához nagy segítséget jelenthet a 10. gyakorlat anyaga, meg persze úgy alapjáraton az eddig vett anyag. Minden szükséges anyag és példa megtalálható a honlapomon.
Beküldési határidő: 2017. 11. 13., éjfél .
Egy lehetséges futási eredmény:
_ _ _ _ _ _ _ Tippelj egy betut: l Eltalaltad az adott karaktert, lephetsz tovabb! l _ _ _ _ _ _ Tippelj egy betut: v Nem talalt! Meg 5 lehetoseg az adott karakterre! l _ _ _ _ _ _ Tippelj egy betut: m Nem talalt! Meg 4 lehetoseg az adott karakterre! l _ _ _ _ _ _ Tippelj egy betut: o Eltalaltad az adott karaktert, lephetsz tovabb! l o _ _ _ _ _ Tippelj egy betut: v Eltalaltad az adott karaktert, lephetsz tovabb! l o v _ _ _ _ Tippelj egy betut: a Eltalaltad az adott karaktert, lephetsz tovabb! l o v a _ _ _ Tippelj egy betut: t Nem talalt! Meg 5 lehetoseg az adott karakterre! l o v a _ _ _ Tippelj egy betut: r Nem talalt! Meg 4 lehetoseg az adott karakterre! l o v a _ _ _ Tippelj egy betut: e Nem talalt! Meg 3 lehetoseg az adott karakterre! l o v a _ _ _ Tippelj egy betut: g Eltalaltad az adott karaktert, lephetsz tovabb! l o v a g _ _ Tippelj egy betut: o Eltalaltad az adott karaktert, lephetsz tovabb! l o v a g o _ Tippelj egy betut: n Nem talalt! Meg 5 lehetoseg az adott karakterre! l o v a g o _ Tippelj egy betut: k Gratulalok! Kitalaltad a keresett szot, amely "lovagok" volt!
11. gyakorlat
FILE IO - példa a nagyZH-hoz
// !
/*
Pelda a [7307] feladat FILE IO muveleteire.
Hasonlokeppen oldhato meg az osszes tobbi feladat eseten is.
[7307]
be.txt:
1 11
EZAKULCSSZO
menekuljetekmertjonazellenseg
ki.txt:
qdnoefnbwssolebnuqfsyspkexmpi
*/
#include <stdio.h>
#include <string.h> // Ezt a header-t nem hasznalhatjatok, en csak a tenyleges szamitasok helyett hasznalom
int main () {
/* Valtozok letrehozasa */
int kodolase, hossz;
char kulcsszo[27], szoveg[201], eredmeny[201];
FILE *in, *out;
in = fopen("be.txt","r");
out = fopen("ki.txt", "w");
/* Beolvasas */
fscanf(in, "%d%d%s%s",&kodolase, &hossz, kulcsszo, szoveg);
/* Beolvasott valtozok ellenorzese */
printf("%d %d\n%s\n%s\n", kodolase, hossz, kulcsszo, szoveg);
/* Kodolt vagy dekolot szoveg eloallitasa */
// . . .
// AZ ALABBI SOR NALATOK NEM SZEREPEL, HELYETTE A TENYLEGES SZAMITASOKKAL ELOALL AZ EREDMENY TOMB
strcpy(eredmeny, "qdnoefnbwssolebnuqfsyspkexmpi");
// . . .
/* Kapott szoveg kiiratasa */
fprintf(out, "%s\n", eredmeny); // FONTOS, az uj sor ne maradjon le! (\n)
fclose(in);
fclose(out);
return 0;
}
Pointer haladó
// !
F: Dinamikus kétdimenziós tömb létrehozása
==============================================================================
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p, **t;
int N=3, M=4;
int i, j, v=0;
/* V1: egydimenziós tömb */
p=malloc(N*M*sizeof(int));
for(i=0; i<N; i++) {
for(j=0; j<M; j++) {
p[i*M+j]=v++;
}
}
free(p);
/* V2: sorokat külön-külön */
t=malloc(N*sizeof(int*));
for(i=0; i<N; i++) {
t[i]=malloc(M*sizeof(int));
}
for(i=0; i<N; i++) {
for(j=0; j<M; j++) {
t[i][j]=v++;
}
}
for(i=0; i<N; i++) {
free(t[i]);
}
free(t);
return 0;
}
Gyakorló feladat
! Feladat: Invertálj egy PGM képet, azaz minden képpont intenzitását változtasd az ellenkezőjére (így pl. feketéből fehér, világosból sötét lesz, mint a fotónegatívokon).
Specifikáció: A program inputja egy (egyszerűsített) PGM formátumú kép. A PGM formátum egy szürkeárnyalatos képet ír le a következő formában. A PGM fájl első sora a "P2" szöveg; a második sortól kezdve egész értékeket tartalmaz whitespace karakterekkel (szóköz, tabulátor, sortörés, stb.) elválasztva. Az első két érték X és Y, a kép szélessége és magassága, a harmadik M, a képpontok lehetséges maximális értéke. Ezt további X*Y darab [0..M] intervallumba eső érték követi, az egyes képpontok intenzitásértékei, sorfolytonosan megadva. A kimenet egy PGM formátumú kép, az eredeti kép inverze.
Használj strukturát a pgm formátumú kép tárolására!
Dinamikus memóriafoglalással oldjuk meg a feladatot!
A program teszteléséhez használjuk az alábbi fájlt: progalap.pgm
Egy lehetséges megoldás: pgmInverter.c
Láncolt lista
F: Írj egy programot, ami egy láncolt listába olvas be egész számokat egy
konstansként megadott végjelig, majd fordított sorrendben kiírja a
beolvasott értékeket.
==============================================================================
== BEGIN linkedlist.c ========================================================
#include <stdio.h>
#include <stdlib.h>
#define VEGJEL 0
struct cella {
int ertek;
struct cella *kov;
};
int main()
{
struct cella *elso = NULL; // ezt a mutatot hasznaljuk majd az elso elemre valo mutatasra minden korben
struct cella *p; // ezt a mutatot hasznaljuk majd az aktualis elem lementeserr
int input; // ide olvasunk majd be ertekeket
scanf("%d", &input); // indito beolvasas
while(input!=VEGJEL) { // minden korben leellenorizzuk, hogy a vegjelet olvastuk-e be (ami ugyebar a 0)
// ha nem végjel, akkor:
p = malloc(sizeof(struct cella)); // foglalunk neki helyet (egy stuktura meretet), ahova a p mutat
p->ertek = input; // a beolvasott ertek lesz az uj struct elem erteke
p->kov = elso; // raalitjuk az elozo korben beolvasott elemre
elso = p; // azt mondjuk, hogy a most beolvasott elem lesz aktualisan az elso
scanf("%d", &input); // kovetkezo input beolvasasa
}
/* kiiratas visszafele
tudjuk, hogy az "elso" pointer mutat a legutoljara beolvasott elemre es
mivel visszafele akarunk kiiratni, ezert ettol az elemtol indulunk, ezert p=elso a kezdoertek
tudjuk, hogy a legelso elem NULL-ra mutat,
mivel belallitottuk a main elso soraban, ezert p!=NULL a feltetel
tudjuk, hogy a kovetkezo elem a "kov" valtozoban van benne,
mivel a while harmadik parancsakent raallitottuk, ezert p=p->kov a leptetes
*/
for(p=elso; p!=NULL; p=p->kov) {
printf("%d\n", p->ertek);
}
// Lefoglalt memoria teruletek felszabaditasa
// Itt ha csak az elozo ciklust hasznalnak, akkor mindig felszabadulna az a terulet ami alapjan a kovetkezo elemre ugranank
while(elso!=NULL) {
p =elso; // aktualis elemre valo hivatkozas
elso=p->kov; // kovetkezo ellem kimentese
free(p); // aktualis terulet felszabaditasa (a kovetkezo elem mem. cimevel egyutt)
}
return 0;
}
Függvény pointer
Több feladat közül futási időben döntöm el, hogy melyiket hajtom végre. A függvényre mutató pointer a függvény kódjának a címére mutat, azon keresztül meghívhatom a függvényt. A pointernek tudnia kell a függvény típusát (paraméterek és visszatérési érték típusa). Pl.:
double fgv(double, double); /* függvény deklarációja */ double (*fptr)(double, double); /* ilyen típusú függvényre mutató pointer deklarációja */ fptr = fgv; /* a függvény nevét adom kezdőértékül, a fordító persze ebből címet állít elő */ fptr(x, y); /* meghívom a függvényt a pointeren keresztül */
F: Egy tömbben soroljunk fel függvényeket, és hívjuk meg valahányadikat.
==============================================================================
#include <stdio.h>
#include <math.h>
/* sinus négyzet - beépített függvénnyel */
double sin2(double x) {
double sinx = sin(x);
return sinx*sinx;
}
/* kettes alapú logaritmus - beépített függvénnyel */
double log2(double x) {
return log(x) / log(2);
}
/* cosinus négyzet - beépített függvénnyel */
double cos2(double x) {
double cosx = cos(x);
return cosx*cosx;
}
typedef double (*fgvtip)(double); /* függvényre mutató pointer típusa */
/* melynek double a visszatérési értéke és double típust vár a paraméterlistában */
fgvtip tabla[] = { /* ilyen pointerek tömbje */
sin2, /* a függvény neve értékül adható függvényre mutató pointernek */
log2,
cos2
};
int main() {
int f, x;
char* valasztek = "{ 1: sin2x }\n{ 2: log2x }\n{ 3: cos2x }\n"; /* karakterekre mutató pointer, lényegileg egy string */
printf("Melyik fuggveny legyen?:\n%s?:\t", valasztek);
scanf("%d", &f);
printf("Argumentum erteke?:\t");
scanf("%d", &x);
printf("A fuggveny erteke az adott pontban:\t%lf\n", tabla[f-1](x) );
/* a tömbben lévő pointeren keresztül hívom a függvényt */
/* 1-től 3-ig várt bemenetet a program.
0-tól való indexelés miatt levon 1-et [f-1]
és paraméterként átadódik az úgyszint beolvasott érték (x) */
return 0;
}
Egy egyszerűbb példa:
#include <stdio.h>
#include <time.h>
/* Csinálunk 4 fuggvenyt, ahol mindegyiknek megegyező lesz a tipusa es a bemeneti parametereknek a tipusa illetve szama is */
/* Egyszeru pelda reven legyen ez a 4 fuggveny a 4 alapmuvelet */
int osszead (int a, int b){
return a+b;
}
int kivon (int a, int b){
return a-b;
}
int szoroz (int a, int b){
return a*b;
}
int oszt (int a, int b){
if (b > 0) // mivel 0-val nem osztunk!
return a/b;
else
return 0;
}
/* hozzuk letre az ilyen fuggvenyekre mutato pointer tipust */
typedef int (*muveletekPointer)(int, int);
/* hozzunk letre egy tombot, mely az elozoleg definialt tipusu legyen,
es a 4 eleme a 4 fuggvenyunk neve legyen */
muveletekPointer muveletekTomb[] = {osszead, kivon, szoroz, oszt};
int main(){
srand(time(NULL));
int a, b, eredmeny, // 3 segedvaltozo a 2 bemeneti paramterhez es a visszaadott ertekhez
random = rand()%4; // random szam sorsolasa 0 es 3 kozott [0,3]
printf("Add meg az elso parametert: ");
scanf("%d",&a);
printf("Add meg a masodik parametert: ");
scanf("%d",&b);
/* a radnom elem alapjan kivalasztott eleme a tombnek az adott sorszamu fuggveny,
mely megkapja az elozoleg beolvasott ket erteket bemeneti parameternek.
Mint latszik a fuggvenyek hasonlosagabol adodoan az osszes fuggvenyre raillik ez a hivasmod */
eredmeny = muveletekTomb[random](a, b);
/* csak hogy legyen egy kis regen latott vezerlesi szerkezet,
az alapjan, hogy melyik muveletet sorsoltuk,
az alapjan valtozik az eredmeny kiiratasa */
switch(random){
case 0: printf("A ket szam osszege: "); break;
case 1: printf("A ket szam kulonbsege: "); break;
case 2: printf("A ket szam szorzata: "); break;
case 3: printf("A ket szam hanyadosa: "); break;
}
printf("%d\n\n", eredmeny);
return 0;
}
12. gyakorlat
Felsorolás típus - enum
Felsorolás adattípus értékhalmaza a típusképzésben felsorolt azonosítók, mint konstans azonosítók által meghatározott értékek.
Feladat: Definiálj egy felsorolástípust a hét napjainak tárolására, majd írasd ki a napok értékeit!
#include <stdio.h>
int main()
{
enum { Hetfo,
Kedd,
Szerda,
Csutortok,
Pentek,
Szombat,
Vasarnap
} nap;
for(nap=Hetfo; nap <= Vasarnap; nap++) {
printf("%d\n", nap);
}
return 0;
}
Feladat: Vedd külön a típusdefiníciót és a változódeklarációt!
#include <stdio.h>
typedef enum { Hetfo,
Kedd,
Szerda,
Csutortok,
Pentek,
Szombat,
Vasarnap
} het;
int main()
{
het nap;
for(nap=Hetfo; nap <= Vasarnap; nap++) {
printf("%d\n", nap);
}
return 0;
}
Egy enum változó tulajdonképpen egy egész változó.
Elmélkedjünk:
F: Mi történik, ha Hetfo=1 -ként adod meg az első elemet? F: Mi történik, ha Szombat=10 -ként adod meg a hatodik elemet? F: Adhatod-e az enum mindegyik elemének ugyanazt az int értéket?
Parancssori argumentumok
A main függvénynek három paramétere lehet. Az első egy int, a parancssorban kapott argumentumok száma + 1 (a program neve maga is egy argumentum). A második egy olyan pointer-tömb, mely azokra a memóriaterületekre mutatnak, ahol sztringként vannak letárolva a parancssori argumentumok. A harmadik hasonló a másodikhoz, csak ez a környezeti változók címeit tartalmazza. Mi csak az első kettővel foglalkozunk.
Írjuk ki a parancssorban lévő argumentumokat:
#include <stdio.h>
int main(int argc, char **argv) // tehát itt látható a main függvény alapértelmezett parancsori paramétereinek használata
// vagy így is lehet az argv-t deklarálni:
// main(int argc, char *argv[])
{
int i;
printf("argc = %d\n\n",argc);
for (i=0; i<argc; ++i) {
printf("argv[%d]: %s\n", i, argv[i]);
}
return 0;
}
Mentsük el a fenti programot arg.c néven és fordítsuk le!
fordítás: $ gcc -o arg arg.c futtatás: $ ./arg alma korte szilva barack palinka argc = 6 argv[0]: ./arg argv[1]: alma argv[2]: korte argv[3]: szilva argv[4]: barack argv[5]: palinka
F: Írj egy programot, ami összeadja a parancssori paramétereket.
==============================================================================
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int i;
int arg = 0;
printf("Osszesen %d programargumentumot kaptam!\n",argc);
for(i = 0; i< argc; i++)
printf("%d : %s\n",i, argv[i]);
if(argc > 1)
{
for(i = 1; i< argc; i++)
arg += atoi(argv[i]); // az atoi fuggveny atkonvertalja a megadott karaktersorozat típusú parametert szamma
}
printf("Az argumentumok osszege : %d\n", arg);
return 0;
}
Modulok
Egy C program egy vagy több modulból áll. A modulok valamelyikének tartalmaznia kell a main függvényt, ami továbbra is a program belépési pontja lesz, vagyis ez kezd el végrehajtódni. Minden modul külön file, de függhetnek egymástól. Több modul használata nagy programok esetén szükségszerű. A moduláris programozás lényege, hogy minden modul önálló fordítási egységet képez, melyeknél érvényesül az adatrejtés elve.
Tehát lényegileg több forrásból szeretnénk felépíteni egy programot. Az előállított header fájlokat a program elején tudjuk be include-olni. Így tudjuk elérni a "külső" fájlokban lévő szügkséges kódrészeket.
Az alábbi példa egy 3 fájlból felépülő programot mutat be:
A következő 3 fájl felépítése:
- lib.h : függvények deklarációja, a függvényekhez kommentek - lib.c : a lib.h -ban deklarált függvények implementálása - libmain.c : olyan program, amely használja a "lib" függvénykönyvtárunkat
Elkészítés a következőképpen zajlik:
$ gcc -o futtathato lib.c libmain.c
Tehát csak fel kell sorolni az összes source fájlt, melyet használ a programunk.
==============================================================================
== BEGIN lib.h ===============================================================
#ifndef LIB_H
#define LIB_H 1
/*
* Olyan függvény, mely az első paraméterében kapott sztringet megfordítva
* beleteszi a második paraméterében kapott sztringbe.
* */
void megfordit(char *str, char *forditott);
/*
* Olyan függvény, amely kiszámolja a paraméterében kapott tömb átlagát.
* */
float atlag(int *t, int meret);
#endif
== END lib.h =================================================================
==============================================================================
==============================================================================
== BEGIN lib.c ===============================================================
#include "lib.h"
void megfordit(char *str, char *forditott)
{
int i,j;
for(i=0;str[i]!='\0';i++)
;
i--;
for(j=0;i>=0;--i,j++)
forditott[j] = str[i];
forditott[j] = '\0';
}
float atlag(int *t, int meret)
{
float atlag = 0.0;
int i=0;
while(i<meret)
{
atlag += *(t+i);
i++;
}
atlag /= meret;
return atlag;
}
== END lib.c =================================================================
==============================================================================
==============================================================================
== BEGIN libmain.c ===========================================================
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "lib.h"
int main()
{
char *sz1 = "Discovery Channel";
char *sz2 = (char*)calloc(strlen(sz1)+1,sizeof(char));
int tomb[] = {-2, 10, 23, -45, 67, 0, 0, 34, 99 };
megfordit(sz1,sz2);
printf("%s megfordítva : %s\n",sz1,sz2);
free(sz2);
printf("A tömb átlaga : %f\n",atlag(tomb,9));
return 0;
}
== END libmain.c =============================================================
==============================================================================
Egy nagyon egyszerű példa:
my.h fájl tartalma
void kiir();
my.c fájl tartalma
#include <stdio.h>
#include "my.h"
void kiir(){
printf("Ez egy kulso header fajl, amit en csinaltam. Hahh!\n");
}
main.c fájl tartalma
#include <stdio.h>
#include "my.h"
int main(){
kiir();
return 0;
}
Makrók
A #define direktívákat arra használjuk, hogy "beszédes" azonosítókkal lássunk el C konstansokat, kulcsszavakat, illetve gyakran használt utasításokat és kifejezéseket. A makrónevekre ugyanaz a képzési szabály vonatkozik, mint más azonosítókra. A makróneveket csupa nagy betűvel ajánlott írni, hogy a szövegben elkülönüljenek a programban használt azonosítóktól.
Az alábbi program meghatározza két szám közül, hogy melyik a kisebb.
#include<stdio.h>
#define min(X,Y) ( (X)<(Y) ? (X) : (Y)) // makró - paraméterben kaphat két számot. A visszatérési értéke az utóbbi zárojelben lévő kiértékelés eredménye
int main() {
printf("%d\n",min(4,34));
return 0;
}
Az előfeldolgozó (preprocesszor) minden programsort átvizsgál, hogy az tartalmaz-e valamilyen korábban definiált makrónevet. Ha igen, akkor azt lecseréli a megfelelő helyettesítő szövegre, majd halad tovább a vizsgálattal.
A preprocesszálás eredménye megtekinthető (mindig a frissen létrejövő *.i fájl végén található a saját kódunk - ez a mi esetünkben az utolsó 4 sor):
gcc -E makrofgv.c|tail -4>makrofgv.i
Írjuk meg a negyzet(a) makrót!
#include <stdio.h>
#define negyzet(a) (a*a)
int main()
{
int a = 5;
printf("negyzet(%d) = %d",a,negyzet(a));
return 0;
}
IF-ELSE kicsit másképp
Lehetőségünk van makrók segítségével megválasztani, hogy mely programrészek fussanak le és melyek ne.
Az alábbi programban szerepel egy #define, mellyel létrehozzuk a TRIAL_VERSION nevű makrót. Ezután található egy #ifdef parancs mely egy makrót vár közvetlen utána. Abban az esetben, ha a megadott makró létezik, akkor lefut az #ifdef utáni rész. Abban az esetben, ha ez a makró nem létezik, akkor az #else után szerepló kódrészlet fog lefutni. Egy #ifdef parancsot mindig egy #endif kell, hogy zárjon.
#include <stdio.h>
#define TRIAL_VERSION 1
#ifdef TRIAL_VERSION
void calculate(int a,int b)
{
printf("Ez csak próbaverzió! Az összes funkció eléréséhez fizess!\n");
}
#else
void calculate(int a,int b)
{
printf("%d és %d számtani közepe : %f\n",a,b,(float)a/2 + (float)b/2);
}
#endif
int main()
{
calculate(10,20);
return 0;
}
Használható még az #if és #elif parancsok is, melyek az if és else if parancsnak felelnek meg. Valamint az #ifndef parancs, mely alapból egy makró definiálást vár és az utána lévő rész egész a következő #endif-ig lefog futni, ha csak nincs közben (tehát benne) másik, az előbbihez hasonló "if" ág.
Tárolási osztályok
Tárolási osztályokat bemutató program
==============================================================================
#include <stdio.h>
#define MAX_NUM 5
/* A static tárolási osztály azt jelenti, hogy az adott változó megmarad a
* blokkból kilépés után is, és a következő belépéskor elérhető lesz a
* legutóbbi tartalom.
*
* Használhatjuk arra, hogy megszámoljuk, hányszor hívtuk az adott függvényt.
*/
int* counter()
{
static int count = 0;
count++;
printf("Count : %d\n",count);
return &count;
}
int* kell()
{
return counter();
}
int main()
{
/* Az auto tárolási osztály az alapértelmezett, ki sem szükséges tenni.
*/
auto int valami = 10;
/* A register tárolási osztály arra szolgál, hogy jelezzük a fordítónak,
* hogy olyan gépi kódot PRÓBÁLJON meg csinálni, amely során ez a változó
* folyamatosan a CPU egy regiszterében van. -> Gyorsabb elérésű, mint a
* memória, de sokkal kevesebb ilyen változó létezhet. Gyakran változó
* változót érdemes.
*
* A fordító figyelmen kívül hagyhatja!
*/
register int i;
/* A volatile módosító azt mondja a fordítónak, hogy:
* "Vigyázat, ez a változó értéke úgy is módosulhat, hogy a kódban nincsen
* annak módosítására szolgáló utasítás!"
* Pl. a változó egy porthoz csatlakozik, ahová az adott eszköz írhat!
*/
volatile unsigned short int device = (unsigned short int)0;
/* A const módosító azt mondja a fordítónak, hogy az érték nem
* megváltoztatható. Ez viszont csak annyit jelent, hogy az adott
* változóhivatkozás nem szerepelhet értékadás bal oldalán.
*/
const long int nemvaltozo = 2007;
int *p;
p=&i; /* EZ HIBÁS */
p=counter();
printf("*%p = %d\n", p, *p);
p=kell();
printf("*%p = %d\n", p, *p);
*p = 100;
counter();
return 0;
}
C kód szintaxis kiemelő stílus csere