Gábor LÉKÓ, PhD student

Programozás alapjai gyakorlat 2016-2017/1


Az alap kurzusrész tematikája:
 * Bevezetés
 * Linux operációs rendszer alapjai
 * 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, helyességigazolá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) [2016-09-05]:
     2. (37) [2016-09-12]:
     3. (38) [2016-09-19]:
     4. (39) [2016-09-26]: 1. mini zh	//	5p
     5. (40) [2016-10-03]: 2. mini zh	//	5p
     6. (41) [2016-10-10]: 3. mini zh	//	5p
     7. (42) [2016-10-17]: 4. mini zh	//	5p
     8. (43) [2016-10-24]: 5. mini zh	//	10p
     9. (44) [2016-10-31]:
    10. (45) [2016-11-07]: 6. mini zh	//	10p
    11. (46) [2016-11-14]: 7. mini zh	//	10p
    12. (47) [2016-11-21]: 8. mini zh	//	10p
    13. (48) [2016-11-28]: Nagy zh	//	20p
    14. (49) [2016-12-05]: Javító

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.05./09.06. - Évkezdési adminisztárció + Linux alapok 1/2: fájlkezelés, felhasználók, multitask

  2. gyakorlat - 09.12./09.13. - Linux alapok 2/2: szövegfájlok, mintaillesztés, bash, hálózatok

C programozási nyelv

  3. gyakorlat - 09.19./09.20. - fordítás & futtatás, változók ( lokális & globális ), input/output, blokkok, függvények

  4. gyakorlat - 09.26./09.27. - 1. miniZH (5p) - if-else, feltételes kifejezések, switch, while, do-while, for

  5. gyakorlat - 10.03./10.04. - 2. miniZH (5p) - konstansok, tömbök, karaktertömbök

  6. gyakorlat - 10.10./10.11. - 3. miniZH (5p) - típusok haladó: typedef, enum, char, float, int

  7. gyakorlat - 10.17./10.18. - 4. miniZH (5p) - függvények haladó, rekurzió, I/O haladó, I/O FILE

  8. gyakorlat - 10.24./10.25. - 5. miniZH (10p) - struct, union, pointer

  9. gyakorlat - 10.31./11.01. - Elmarad mindkét csoportnak!

10. gyakorlat - 11.07./11.08. - 6. miniZH (10p) - pointer haladó, deklaráció haladó, tárolási osztályok

11. gyakorlat - 11.14./11.15. - 7. miniZH (10p) - függvénypointer, parancssori argumentumok

12. gyakorlat - 11.21./11.22. - 8. miniZH (10p) - makrók, if-else kicsit másképp, modulok

13. gyakorlat - 11.28./11.29. - NagyZH (20p)

14. gyakorlat - 12.05./12.06. - 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.: Karacsonyi.Szilveszter.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 csoport esetén: [progalap2016][H][24][reg]
      • Keddi csoport esetén: [progalap2016][K][26][reg]
    • Tartalom: h-s azonosító;EHA-kód;név . ( Példa: h123456;LEGTAAT.SZE;Lékó Gábor )

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 1/2

Debian, 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 .

A weboldalon fellelhető anyag fő forrása .

Linux parancsok itt .

Gyakorlásra és ZH-ra készülésre magát a weboldalt ajánlanám, mivel minden ami a ZH-ban vissza lesz kérve a Linux parancsokról, itt megtalálható.



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.




Felhasználók

  • finger <argumentum>

  • Kiírja, hogy kik vannak bejelentkezve az aktuális gépre.

    felhasználó: a megadott felhasználóról ír ki adatokat.


  • who

  • Kiírja, hogy kik vannak bejelentkezve az aktuális gépre, plusz adatokat ír ki a finger-hez képest.


  • w

  • Ugyanaz, mint a who, csak kiírja, hogy min dolgozik.




Multitask

  • ps

  • 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.


  • jobs

  • Kiírja az aktuális jobokat, amik az adott terminálhoz kapcsolódnak.


  • ^C

  • Ctrl+c paranccsal leállíthatunk előtérben futó folyamatokat.


  • ^Z

  • Ctrl+z paranccsal szüneteltethetünk előtérben futó folyamatokat.


  • bg %<szám>

  • 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").


  • fg %<szám>

  • 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").


  • kill %<szám>/PID

  • 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.





    Még több feladat található a PUB-ban ( /n/pub/ProgramozasAlapjai/Gyakorlat/gyak01 ) !



2. gyakorlat





Linux alapok 2/2

Továbbra is a Linux operációs rendszer alapparancsaival ismerkedünk.



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:~$
    




    Még több feladat található a PUB-ban ( /n/pub/ProgramozasAlapjai/Gyakorlat/gyak02 ) !



3. gyakorlat




    C alapok


GCC Windows-ra: Code::Blocks , Cygwin

ASCII kódtábla

C könyvtár-referencia

Gyakorlaton bemutatott könyv itt és egy újabb kiadás itt




    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:


#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");
}

Adatok bekérése:


#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!






Még több feladat található a PUB-ban ( /n/pub/ProgramozasAlapjai/Gyakorlat/gyak03 ) !





    Jövő héten 1. miniZH! (2016.09.26./27.)

Téma:

  Linux alapok: 1. és 2. gyakorlat anyaga.

  Gyakorlásra:

  • A honlapomon az 1. és 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).

Egyéb infó:

Előreláthatóan 30 percetek lesz a feladatok megoldására és beadására (tehát hétfő - 15:30-ig/ kedd - 17:30-ig). A feladatokat a BÍRÓ rendszeren keresztül fogjátok megkapni és beadni is, de értékelni (javítani) én fogom. 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: Írjunk olyan programot amely bekér egy karaktert, majd kiírja azt. Használjunk blokkokat, lokális és globális változókat. Mindezt egy függvényben valósítsuk meg, melyhez írunk egy programot is, amely ezt a függvényt használja.

Megoldás letöltése .





    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
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

Írjunk egy programot, mely egy cikluson belül addig kér be karaktereket, amíg 0-át nem kap. A beolvasott karakterek ASCII kódbeli számértékeinek felhasználásával számítsuk ki a beolvasott karakterek átlagát. Természetesen az utolsó 0 karakterkódja már nem számítson bele.

Pl.: bela -> b = 98, e = 101, l = 108, a = 97 -> 98+101+108+97 = 404/4 = 101

Egy lehetséges megoldás karakter_atlag.c .





Plusz pontos feladat

Írjunk egy játékot! A játék a jól ismert "Találd ki, hogy milyen számra gondoltam".

A játékot a számítógép ellen játszuk, tehát a gép gondol egy számra 1 és 100 között, nekünk pedig ki kell találni. Addig találgassunk, amíg bele nem trafálunk. Minden egyes tippünknél közölje velünk a gép, hogy alá vagy fölé lőttünk, így szép lassan biztos meg lesz a helyes eredmény. Természetesen ahhoz, hogy a gép kitaláljon egy számot szükségünk van egy random generátorra. Ehhez egy kis segítség:
  Include-oljuk a <time.h> header-t
  A main első sora legyen: srand(time(NULL));
  A random számot 1 és 100 között pedig az alábbival képezzük: (rand() % 100)+1
Tehát szükségünk lesz a programhoz a fent megadottakra, valamint egy ciklusra, ami addig fut míg ki nem találtuk a számot, szükség lesz továbbá felhasználói interakciókra (kiíratásra és beolvasásra), és vezérlési szerkezetre a tipp és a kigondolt szám összehasonlítására. Kommenteljünk is a kódban!

Egy lehetséges megoldás: jatek.c





    Jövő héten 2. miniZH (2016.10.03./04.)

Téma:

  C programozás (ezután mindig az lesz): 3. gyakorlat anyaga.

  Gyakorlásra:

    A honlapomon a 3. gyakorlathoz tartozó anyag, magyarázatokkal, példákkal (egy ismétléses példa a 4. gyakorlat elején).

    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 )

  ( 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ő - 15:30-ig/ kedd - 17: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, mivel 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




    Mit is tanultunk a 4. 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). Feltételes kifejezéssel írasd ki, hogy mit választott a hallgató.

    (c) Kezdőfeltételes ismétléses vezérléssel kérj be ciklusonként 5 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 .





    Konstansok


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;
}

Az ilyen konstansokat a preprocesszor kezeli, ugyanaz, "aki" a #include -ot is.

$ gcc -E konstans.c >konstans.i
$ gcc konstans.i -o konstans

Nézzük meg a konstans.i végét! Felismerjük a saját kódunkat? Ami előtte van, az az #include <stdio.h> -ból jön.

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;
}



    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: Módosítsd a programot úgy, hogy a következő sorba csak a "Hello" szöveget írja ki!

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;
}





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 .





Plusz pontos házi 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.
    FIFO-ról bővebben: itt

Beküldési határidő: hétfőieknek 2016. 10. 07., éjfél, keddieknek 2016. 10. 08., éjfél.

A kódot kommentelni kell!

Küldés STUD-os email címről, melynek tárgya: [progalap2016][05][plusz], tartalma pedig maga a kód, vagy a csatolt .c fájl.

Egy lehetséges megoldás: fifo.c






    Jövő héten 3. miniZH (2016.10.10./11.)

Téma:

  4. gyakorlat anyaga. (De tudni kell az előző - tehát a 3. - gyakorlat anyagát is!)

  Gyakorlásra:

    A honlapomon a 3. és 4. gyakorlathoz tartozó anyag, magyarázatokkal, példákkal.

    A gyakorlatok végén lévő feladatok megoldása.

    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 35 percetek lesz a feladatok megoldására és beadására (tehát 15:35-ig/17:35-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ó!


6. gyakorlat




    Mit is tanultunk az 5. gyakorlaton?


Ismétlő feladat:

    (a) Hozzunk létre egy 1D és egy 2D integer típusú tömböt. A méretek megadásához használjunk konstansokat. Az 1D tömböt töltsük fel 1-től 10-ig az egész számokkal, a 2D tömböt pedig a 10*10-es szorzótáblával. Írassuk is ki a kapott tömbök elemeinek értékét.

    (b) 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.

Megoldás letöltése .





    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;



    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?

Nézzük meg a következő összetettebb példát. Mi is történik és miért?


#include <stdio.h>

int main()
{
    enum het { Hetfo, Kedd, Szerda, Csutortok, Pentek, Szombat, Vasarnap } nap;
    typedef enum { piros, zold, sarga } colors;
    colors col;

    printf("Milyen napon szeretnél almát enni? "); scanf("%d",&nap);
    printf("Milyen színű almát szeretnél enni? "); scanf("%d",&col);
    switch(nap)
    {
        case Hetfo :
        case Kedd :
        case Szerda :
        case Csutortok : 
        case Pentek :
            printf("Csak hétvégén tudok almát felszolgálni!\n");
            break;
        case Szombat :
        case Vasarnap :
            printf("Mivel hétvége van, alma is van!\n");
            switch(col)
            {
                case piros: 
                    printf("A piros alma egészséges, jó választás!\n");
                    break;
                case zold :
                    printf("Vigyázz, a zöldalma savanyú!\n");
                    break;
                case sarga :
                    printf("A sárga alma is nagyon finom!\n");
                    break;
                default :
                    printf("Nem ismerek ilyen színű almát!\n");
            }
            break;
        default:
            printf("A hét csak 7 napból áll!\n");
            break;
    }
    
    return 0;
}



    Karakter 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 típus?
==============================================================================

#include <stdio.h>

int main() {
	printf("char: %d\n", sizeof(char));
	return 0;
}

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;
}
F: 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("%hhd %hhu\n", sc, uc);
	}
	return 0;
}

Feladat: Olvass be két legfeljebb 20 karakter hosszúságú szót, és fűzd őket egymás után egy harmadik sztringbe. A string.h függvényeit használd!


#include <stdio.h>
#include <string.h>

int main() {
	char egyik[21], masik[21], harmadik[41];
	scanf("%s %s", egyik, masik);
	strcpy(harmadik, egyik);
	strcat(harmadik, masik);
	printf(" -> %s\n", harmadik);
	return 0;
}



    Float



F: Hány bájton tárolódik a float és double típus?
==============================================================================

#include <stdio.h>

int main() {
	printf("float : %d\n", sizeof(float));
	printf("double: %d\n", sizeof(double));
	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: Mi a különbség a float és a double értékkészlete között? Szorozgasd egy
   float és double változó értékét 0.1-gyel, amíg 0 nem lesz mindkettő. Milyen
   értékeket kapsz lépésenként?
==============================================================================

#include <stdio.h>

int main() {
	float  f = 1.0;
	double d = 1.0;
	int i = 0;
	do {
		printf("%d: float: %g; double: %lg\n", ++i, f, d);
		f *= 0.1;
		d *= 0.1;
	} while((f!=0.0) || (d!=0.0));
	return 0;
}



    Integer



F: Hány bájton tárolódik a short int, int, long int, long long típus?
==============================================================================

#include <stdio.h>

int main() {
	printf("short int: %d\n", sizeof(short int));
	printf("int      : %d\n", sizeof(int));
	printf("long int : %d\n", sizeof(long int));
	printf("long long: %d\n", sizeof(long long));
	return 0;
}

F: Mi a különbség ugyanazon típus előjeles és előjeltelen verziója között?
   Deklarálj 6 változót (signed/unsigned, short/long/long long) változót, 0
   kezdőértékkel, és vonj ki belőlük egyet. Milyen értékeket kapsz? Add
   értékül a változóknak a legnagyobb előjelesen ábrázolható értéket (ez fele
   az előjeltelen maximális értéknek), és adj hozzá egyet. Most mik a változók
   értékei?
==============================================================================

#include <stdio.h>

int main() {
	signed short int   ssi = 0;
	unsigned short int usi = 0;
	signed long int    sli = 0;
	unsigned long int  uli = 0;
	signed long long   sll = 0;
	unsigned long long ull = 0;
	ssi -= 1;
	usi -= 1;
	sli -= 1;
	uli -= 1;
	sll -= 1;
	ull -= 1;
	printf("0 mínusz 1\n");
	printf("s16: %hd\n",  ssi);
	printf("u16: %hu\n",  usi);
	printf("s32: %ld\n",  sli);
	printf("u32: %lu\n",  uli);
	printf("s64: %lld\n", sll);
	printf("u64: %llu\n", ull);
	ssi = usi /= 2;
	sli = uli /= 2;
	sll = ull /= 2;
	printf("Legnagyobb ábrázolható előjeles szám...\n");
	printf("s16: %hd\n",  ssi);
	printf("u16: %hu\n",  usi);
	printf("s32: %ld\n",  sli);
	printf("u32: %lu\n",  uli);
	printf("s64: %lld\n", sll);
	printf("u64: %llu\n", ull);
	ssi += 1;
	usi += 1;
	sli += 1;
	uli += 1;
	sll += 1;
	ull += 1;
	printf("... plusz 1\n");
	printf("s16: %hd\n",  ssi);
	printf("u16: %hu\n",  usi);
	printf("s32: %ld\n",  sli);
	printf("u32: %lu\n",  uli);
	printf("s64: %lld\n", sll);
	printf("u64: %llu\n", ull);
	return 0;
}





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.

Megoldás: nnn1.c

(b) Keressük meg egy tömb legnagyobb/legkisebb elemét.

Megoldás: minmax.c

(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. egy adott egész érték. A 4. pedig, hogy a tömbünkben a 3. paraméterben megadott értékű elemeket erre az értékre cseréljük ki.

Megoldás: mitmire.c





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ő: hétfőieknek 2016. 10. 14., éjfél, keddieknek 2016. 10. 15., éjfél.

Küldés STUD-os email címről, melynek tárgya: [progalap2016][06][plusz], tartalma pedig maga a kód, vagy 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 4. miniZH (2016.10.17./18.)

Téma:

  5. gyakorlat anyaga. (De tudni kell az előző - 3. és 4. - gyakorlat anyagát is!)

  Gyakorlásra:

    A honlapomon a 3., 4. és 5. gyakorlathoz tartozó anyag, magyarázatokkal, példákkal.

    A gyakorlatok végén lévő feladatok megoldása.

    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 35 percetek lesz a feladatok megoldására és beadására (tehát 15:35-ig/17:35-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ó!



7. gyakorlat




    Előző heti plusz pontos feladatok:

A megoldások a 6. gyakorlat anyagánál elérhetőek, a feladatkiírások helyén.



    Mit is tanultunk a 6. gyakorlaton?

Ismétlő feladatsort nem állítottam össze. A lényeg, hogy egyszerű típusdefiniálást tudni kell létrehozni, tudni kell használni az enum-felsorolás típust, és jól kell ismerni az egyes típusok méretét és előjeles/előjeltelen formájuk alsó és felső korlátait.




    Függvények haladó


Figyeljük meg, hogy az alábbi programban, nem simán változó értékeket adunk át, hanem memória címeket ( & ). Függvényhíváskor pedig ezekre a memória címekre mutató pointereket ( * ) használunk a változók tényleges értékeinek felülírásához.

A következő gyakorlaton ezt még részletesebben fogjuk tárgyalni.


F: Számítsd ki egy háromszög területét és kerületét a három oldalhossz
segítségével. A számolást egyetlen függvény végezze.

==============================================================================

#include <stdio.h>
#include <math.h>

void haromszogTKpar(double a, double b, double c, double *t, double *k){	// *-gal hivatkozunk az eredeti t és k értékére, a * jelenti a pointert (mutató)
	double s;
	*k = (a + b + c);	// és itt is az eredeti k értéke lesz felül írva
	s  = (*k) / 2.0;
	*t = sqrt((s-a)*(s-b)*(s-c)*s);	// és itt is az eredeti t értéke lesz felül írva
}

int main() {
	double a, b, c, t, k;
	printf("Adja meg az oldalakat!?:\n");
	scanf("%lf %lf %lf", &a, &b, &c);
	haromszogTKpar(a, b, c, &t, &k);	// t és k esetében memória cím átadása, t és k ilyen módon történő megadását referenciának nevezzük

	printf("T: %lf; K: %lf;\n", t, k);
	return 0;
}

==============================================================================

Nézzük meg mi történik, ha nem pointereket használunk.


F: Másodfokú egyenlet megoldása

==============================================================================

#include <stdio.h>
#include <math.h>

int megoldo(double a, double b, double c,  /* együtthatók */
                double *x1, double *x2)             /* gyökök */
{
  double d;                                /* a diszkrimináns */
  int valos;                            /* van-e megoldás */

  valos = 1;
  if (a == 0.0) {
      if (b == 0.0) {                 /* az egyenlet elfajuló */
          valos = 0;
      } else {						  /* 1. fokú */
          *x1 = -(c / b);
          *x2 = *x1;
      }
  } else {
      d = b * b - 4.0 * a * c;
      if (d < 0.0) {                      /* nincs valós gyöke */
          valos = 0;
      } else {
          *x1 = (-b + sqrt(d)) / (2.0 * a);
          *x2 = (-b - sqrt(d)) / (2.0 * a);
      }
  }
  return valos;
}

int main() {
	double a, b, c, x1, x2;
	printf("Adja meg az egyutthatokat!\n?:");
	scanf("%lf", &a); scanf("%lf", &b); scanf("%lf", &c);
	if(megoldo(a, b, c, &x1, &x2))
		printf("Az egyenlet megoldasai: %lf, %lf\n", x1, x2);
	else
		printf("Az egyenletnek nincs valos megoldasa.\n");
	return 0;
}



    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 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
*/


F: Fibonacci-sorozat n. elemének kiszámítása rekurzív módszerrel

=============================================================================

#include <stdio.h>

int fib(int n) {
	if(n==1 || n==2) {
		return 1;
	} else {
		return fib(n-1) + fib(n-2);
	}
}

int main() {
	int n;
	printf("n erteke?: ");
	scanf("%d",&n);
	printf("A fibonacci sorozat %d. eleme: %d\n",n,fib(n));
	return 0;
}

=============================================================================

Kérdés: Hányszor hívódik a függvény?



    Input/Output haladó


Fontos, hogy mekkora méretű típusban mekkora/milyen értéket szeretnénk letárolni. Erre beolvasáskor és kiíratáskor is jelentős figyelmet kell fordítani.

sizeof operátor - típusok méretének meghatározása byte-okban. 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
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


F: Írj egy programot, ami beolvas egy előjeltelen short int értéket, és
   nyolcas számrendszerbe átváltva írja ki.

==============================================================================

#include <stdio.h>

int main() {
	unsigned short int v;
	scanf("%hu", &v);
	printf("%ho\n", v);
	return 0;
}


F: Írj egy programot, ami beolvas egy hexadecimális egész számot, majd 15
   karakter szélességben kiírja a decimális értékét, mindenképpen előjellel és
   vezető nullákkal.

==============================================================================

#include <stdio.h>

int main() {
	unsigned int v;
	scanf("%x", &v);
	printf("%+015u\n", v);
	/*
            + | 0 | 15 | u
		
            15  - 15 helyet hagy ki, majd elkezd visszafelé kiírni
            0   - 0-kal tölti fel a kimaradó helyeket
            u   - átalakítja 10-es számrendszerbe
	*/
	return 0;
}


F: Olvass be egy double és egy egész értéket, majd a valós értéket írasd ki az
   egészben megadott pontossággal.

==============================================================================

#include <stdio.h>

int main() {
	double ertek;
	int pontossag;
	scanf("%lf %d", &ertek, &pontossag);
	printf("%1.*lf\n", pontossag, ertek);
	return 0;
}


F: Olvass be egy csupa kisbetűből álló, legfeljebb 20 karakteres sztringet,
   majd írasd ki 10 karakteren jobbra igazítva az első legfeljebb 8
   karakterét. A bemeneten a kisbetűket közvetlenül bármi követheti.

==============================================================================

#include <stdio.h>

int main() {
	char str[21];
	scanf("%20[a-z]", str);
	printf("%10.8s\n", str);
	return 0;
}

getchar : egy darab karaktert vár a standard bemeneten (egy billentyű lenyomás).

char c = getchar();

putchar : egy darab karaktert ír ki a képernyőre.

putchar(c);


F: Írasd ki a SPACE jelig (32) tartó bemenetet úgy, hogy a számjegyeket
   törlöd belőle. A végén írd ki, hogy hány számjegyet töröltél.

==============================================================================

#include <stdio.h>

int main() {
	int c, d=0;
	while((c=getchar())!=32) {	// bekérésre kerül egy karakter, amely a c változóba kerül lementésre, és ezt hasonlítjuk össze 32-vel
		if('0'<=c && c<='9') {	// ha szám volt, akkor nem írjuk ki ("töröljük"), és növeljük a számlálót
			d++;
		} else {
			putchar(c);	// ellenkező esetben írjuk ki a karaktert a képernyőre
		}
	}
	printf("\n--\n%d torolve\n", d);
	return 0;
}

A C nyelv szabványos könyvtára tartalmaz függvényeket karaktersorozatok (sztringek) egyetlen hívással történő beolvasására (gets) és kiírására (puts).

gets : A gets függvény egy sort (<Enter> lenyomásáig) olvas a szabványos inputról, majd a karaktereket az argumentumban megadott sptr mutató által kijelölt területre másolja:

char* gets(char *sptr);

puts : A puts függvény az argumentumban megadott sztringet a szabványos kimenetre (a képernyőre) írja. A függvény egy nem negatív szám visszaadásával jelzi a sztring sikeres kiíratását, ellenkező esetben pedig EOF (-1) értékkel tér vissza.

int puts(char *sptr);

F: Olvass be egy sztringet, majd írasd ki a képernyőre
   a gets és a puts parancsok használatával.

==============================================================================

#include <stdio.h>

int main() {
	char str[80];
	
	puts("Sztring beolvasasa:");	// string kiíratása
	gets(str);	// string bekérése
	puts(str);	// string kiíratása
    
	return 0;
}



    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.



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;
}

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;
}




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

Fejlesszük tovább úgy, hogy egy rombuszt rajzoljunk ki.

Példa:

Add meg, hogy milyen magas legyen a felso piramis: 5
Add meg milyen karakterbol epuljon fel a rombusz: A
    A
   AAA
  AAAAA
 AAAAAAA
AAAAAAAAA
 AAAAAAA
  AAAAA
   AAA
    A

Egy lehetséges megoldás: piramisRombusz.c





    Jövő héten 5. miniZH (2016.10.24./25.)

Innentől kezdve már 10 pontért!

Téma:

  3. - 6. gyakorlat anyaga.

  Gyakorlásra:

    A honlapomon a 3. - 7. gyakorlathoz tartozó anyag, magyarázatokkal, példákkal.

    A gyakorlatok végén lévő feladatok megoldása.

    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 45 percetek lesz a feladatok megoldására és beadására (tehát 15:45-ig/17:45-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, mivel a bíró rendszer nyit, majd automatikusan zár is. Hiányozni csak igazolással lehet, de a ZH akkor sem pótolható!



8. gyakorlat




    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ó;

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;

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;
}


Egy komolyabb példa:


F: Hozz létre típust egy háromdimenziós térbeli pozíció tárolására. Ezt
   felhasználva hozz létre egy típust, ami részecskék helyzetét, tömegét,
   nevét és töltését (pozitív/negatív/semleges) tárolja. Készíts egy
   függvényt, ami két részecskéről eldönti, hogy melyik nehezebb, és egy
   másikat, ami megmondja, hogy elektromosan vonzzák vagy taszítják egymást,
   esetleg nem hatnak egymásra. Inicializálj két részecskét, és használd a
   függvényeket.

==============================================================================

#include <stdio.h>

typedef struct {
	double x, y, z;
} pozicio;

typedef char nevtipus[30];

typedef enum {negativ = -1, semleges, pozitiv} toltestipus;

typedef struct {
	pozicio helyzet;
	double tomeg;
	nevtipus nev;
	toltestipus toltes;
} reszecske;

int tomeghasonlitas(reszecske a, reszecske b)
{
	if(a.tomeg < b.tomeg) {
		return -1;
	}
	if(a.tomeg > b.tomeg) {
		return 1;
	}
	return 0;
}

int vonzas(reszecske a, reszecske b)
{
	if(a.toltes==semleges || b.toltes==semleges) {
		return 0;
	}
	return (a.toltes==b.toltes)?1:-1;
}

int main()
{
	reszecske p={{0.0, 0.0, 0.0}, 1.0, "proton", pozitiv};
	reszecske e={{1.0, 1.0, 1.0}, 0.001, "elektron", negativ};
	printf("tomeg: %d\nvonzas: %d\n", tomeghasonlitas(p, e), vonzas(p, e));
	return 0;
}


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;
}



    Pointer - alap


  • Ú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.



// !
P: Nézzük meg, 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()
{
	int *p, *q;
	p = malloc(sizeof(int));
	q = malloc(sizeof(int));
	*p = 3;
	*q = 3;
	printf("p es q %s\n", p == q ? "megegyezik" : "nem egyezik meg");
	printf("*p == %d, *q == %d\n", *p, *q);
	*p = 4;
	printf("*p == %d, *q == %d\n", *p, *q);
	free(p);
	p = q;
	printf("p es q %s\n", p == q ? "megegyezik" : "nem egyezik meg");
	printf("*p == %d, *q == %d\n", *p, *q);
	*p = 4;
	printf("*p == %d, *q == %d\n", *p, *q);
	free(p);
	return 0;
}


P: Futtassuk le a következő programot, és értelmezzük! Melyik érték melyik
   értékkel egyenlő, és miért ?

==============================================================================

#include <stdio.h>

int main()
{
	int a = 10;
	int *pa;
	pa = &a;
	printf("%d %#x\n", a, (int)pa);
	printf("%#x %#x\n", (int)&a, (int)&pa);
	printf("%d\n", *pa);
	return 0;
}


// !
F: Írj egy csere() függvényt, ami megcseréli két int típusú változó értékét.

==============================================================================

#include <stdio.h>

void csere(int *x, int *y)
{
	int tmp;
	tmp = *x;
	*x  = *y;
	*y  = tmp;
}

int main()
{
	int x = 3, y = 4;
	printf("A fuggveny elott: x = %d, y = %d\n", x, y);
	csere(&x,&y);
	printf("A fuggveny utan: x = %d, y = %d\n", x, y);
	return 0;
}


// !
F: Deklarálj egy 20 elemű int tömböt, majd töltsd fel értékekkel az inputról.
   Deklarálj egy pointert, és a beolvasást azon keresztül valósítsd meg.

==============================================================================

#include <stdio.h>
#define N 20

int main()
{
	int t[N], *p, i;
	for(i=0; i<N; i++) {
		p=&(t[i]);
		scanf("%d", p);
	}
	for(i=0; i<N; i++) {
		printf("%d\n", t[i]);
	}
	return 0;
}


F: Először olvasd be a tömb méretét, és foglalj neki dinamikusan helyet!

==============================================================================

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int *t, *p, i, N;
	scanf("%d", &N);
	t=(int*)malloc(N*sizeof(int));
	for(i=0; i<N; i++) {
		p=&(t[i]);
		scanf("%d", p);
	}
	for(i=0; i<N; i++) {
		printf("%d\n", t[i]);
	}
	free(t);
	return 0;
}


// !
F: Olvass be 5 darab maximum 99 karakter hosszú szót úgy, hogy mindegyiknek
   pontosan annyi helyet foglalsz, amennyi kell! A sztringeket írasd ki, majd
   szabadítsd fel a lefoglalt területet!

==============================================================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	char buff[100];
	char *ptr_tomb[5];
	int i;
	for(i=0; i<5; i++) {
		scanf("%s", buff);
		ptr_tomb[i] = (char*)malloc(strlen(buff)+1);
		strcpy(ptr_tomb[i], buff);
	}
	for(i=0; i<5; i++) {
		puts(ptr_tomb[i]);
	}
	for(i=0; i<5; i++) {
		free(ptr_tomb[i]);
	}
	return 0;	
}

char Honap[][20]

char *honap[]






Plusz pontos órai feladat

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ó.

Egy lehetséges megoldás mozi1.c .





    Jövő héten elmarad a gyakorlat! (2016.10.31./11.01.)



A gyakorlat elmarad!




Jövő héten 6. miniZH!



10. gyakorlat




    Pointer - haladó



F: Olvasd be egy tömb méretét, foglalj neki dinamikusan helyet, majd olvasd be
   az elemeit!

==============================================================================

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int *t, *p, i, N;
	scanf("%d", &N);
	t=(int*)malloc(N*sizeof(int));
	for(i=0, p=t; i<N; i++, p++) {
		scanf("%d", p);
	}
	for(i=0, p=t; i<N; i++) {
		printf("%d\n", *(p++));
	}
	free(t);
	return 0;
}	 


F: Adott egy kétdimenziós tömb. Pointer segítségével járjuk be az összes
   elemét.

==============================================================================

#include <stdio.h>

#define SIZE 3

int main()
{
    int tomb[SIZE][SIZE] = {{0, 1, 2 },
                            {3, 4, 5 }, 
                            {6, 7, 8 } };
    int i,j;
    int *pa = NULL;

    pa = tomb;  /* pa = &tomb[0][0] */
    for(i = 0; i< SIZE*SIZE; i++)
        printf("%2d ", pa[i]);
    printf("\n");
    for(i = 0; i< SIZE*SIZE; i++)
        printf("%2d ", *(pa+i));
    printf("\n");
    for(i = 0; i< SIZE*SIZE; i++, pa++)
        printf("%2d ", *pa);
    printf("\n");

    /* vigyázat! mivel pa-t növeltük a for ciklusban, ezért a ciklus után már
     * nem a tömb legelső elemére fog mutatni!
     */
    return 0;
}


// !
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;
}


#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i;
    const int maxsize = 100;

    /* Kétdimenziós tömbök */
    float tomb_statikus[100][100];
    float tomb_valtozo_meretu_de_a_gcc_nem_szereti[maxsize][maxsize];
	
    /* 
     * Mivel a program futása során tudjuk meg a maxsize -t (tegyük fel, hgoy így van)
     * dinamikusan kell (így a leghelyesebb, így ragaszkodunk a szabványhoz, és így lesz hordozható a kódunk )
     * helyet foglalni a kétdimenziós tömbünknek.
     *
     * A kétdimenziós tömb olyan egydimenziós tömb, amelynek minden eleme egy egydimenziós tömb.
     * 
     * egydimenziós tömböt tudunk dinamikus létrehozni : 
     *	float *tomb = (float*)calloc(meret, sizeof(float));
     * -> tehát az első egydimenziós tömb elemeit (egydimenziós tömböket) létre tudjuk hozni.
     *  azaz a deklarációnk nézhetne így is ki : float *tomb[meret] (hiszen ez egy pointerekból álló tömb.)
     *  Ezzel nem kerültük ki a meret kiírását ->
     *  csináljunk egy pointert erre a tömbre : float **tomb. 
     *
     *  És KÉSZ!
     * 
     * */

    /* Egy olyan pointer, amely egy pointerre mutat, amely egy float tipusú változó címét tudja tárolni. */
    float **tomb_dinamikus;
	
    /* Foglaljuk le a helyet a külső tömbnek, azaz hozzunk létre egy olyan egydimenziós tömböt,
     * amelynek az elemei olyan pointerek, melyek float változók címeit tudják tárolni*/
    
    tomb_dinamikus = (float**)calloc(maxsize, sizeof(float*));
    
    /* Magyarázat : (float**) : mivel calloc egy void* -al tér vissza, helyes, ha típuskonverziót hajtunk végre.
     *							mi a típusa tomb_dinamikus -nak? float**.
     *				sizeof(float*) :	minden egyes indexén a tomb_dinamikus tömbnek egy 
     *									float-ra mutató pointert akarunk tárolni.
     *									Ezért az alaptípusának a mérete float*, tehát ennek a méretét kell megadnunk.
     * */
	
    /* Foglaljunk helyet a most létrehozott tömb elemeinek (hiszen azok is pointerek) */
    for(i = 0; i< maxsize; i++)
        tomb_dinamikus[i] = (float*)calloc(maxsize, sizeof(float));

    /* Magyarázat : (float*) :  mivel calloc egy void* -al tér vissza, helyes, ha típuskonverziót hajtunk végre.
     *							mi a típusa tomb_dinamikus[i] -nek? float* .
     *				sizeof(float) :	minden tomb_dinamikus[i] egy float-ra mutató pointer.
     *								Ezért az alaptípusának a mérete float, tehát ennek a méretét kell megadnunk.
     * */
	
    tomb_dinamikus[2][5] = 12.45;

    printf("%f\n",tomb_dinamikus[2][5]);

    /* Ha már nem használjuk a tömbünket, fel kell szabadítani az általunk lefoglalt memóriaterületet. 
     * 
     * Először a pointereket tartalmazó tömbben lévő pointereket kell felszabadítanunk, majd utána a tömbnek lefoglalt memóriaterületet.
     * */
	
    for(i = 0; i< maxsize; i++)
        free(tomb_dinamikus[i]);
    free(tomb_dinamikus);	
	
    return 0;	
}



    Deklaráció - haladó



F: Készíts egy pointert, ami egy konstans értékre mutat.
F: Készíts egy konstans pointert, ami egy nem konstans értékre mutat.
F: Készíts egy pointert, ami egy tömbre mutat.

==============================================================================

#include <stdio.h>

int main()
{
    const int *p = NULL;	// pointer, ami egy konstans értékre mutat
    int * const c = NULL;	// konstans pointer, ami egy nem konstans értékre mutat
    int (*t)[20];		// pointer, ami egy tömbre mutat
    
    p = malloc(sizeof(int));
    *p = 2007; /* HIBÁS - mivel konstans értékét nem tudjuk megváltoztatni */
    free(p);
    c = malloc(sizeof(int)); /* HIBÁS */
    *c = 2007;
    free(c);
    return 0;   
}



    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;
}




Plusz pontos órai feladat

Készíts két darab N*N-es integer tömböt, majd töltsd fel az elsőt random 0-kal és 1-esekkel. Írasd ki. Majd keresd meg azokat a pozíciókat, ahol egymás mellett legalább két darab 0 található. Ezeket a helyeket jelöld 0-kal, a többi helyre írj 1-eseket.

Egy lehetséges megoldás mozi2.c .




    Jövő héten 7. miniZH (2016.11.10./12.)

Téma:

  3. - 10. gyakorlat anyaga.

  Gyakorlásra:

    A honlapomon a 3. - 10. gyakorlathoz tartozó anyag, magyarázatokkal, példákkal.

    A gyakorlatok végén lévő feladatok megoldása.

    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 50 percetek lesz a feladatok megoldására és beadására (tehát 15:50-ig/17:50-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, mivel a bíró rendszer nyit, majd automatikusan zár is. Hiányozni csak igazolással lehet, de a ZH akkor sem pótolható!


11. gyakorlat




    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;
}


F: Egy tömbbe olvassunk be, majd írassuk ki az elemeket úgy, hogy a tömb
   bejárását egy függvény végezze, ami megkapja a műveletet.
   
==============================================================================

#include <stdio.h>

#define N 10

typedef void (*muvelettipus)(int*); /* függvényre mutató pointer típusa */
                                    /* melynek nincs visszatérési értéke (void) és int-re mutató pointert vár a paraméterlistában */

/* egy tömböt vár illetve a bejar függvény alatt található művelet függvények egyikét */
void bejar(int* tomb, muvelettipus muvelet) {
	int i;
	for(i=0; i<N; i++) {
		(*muvelet)(tomb+i);   // ide kerül behelyettesítésre a paraméterként kapott művelet-függvény, mely saját bemeneti paraméterét is megkapja.
		                      // azért kerül 'tomb+i' a paraméterlistába, mert a 'tomb' alapjáraton a paraméterként kapott tömb kezdőpozíciójára mutat, 
		                      // így a for ciklusban végig haladva, mindig i-t hazzáadva mindig a tömb rákövetkező elemét kapjuk 
	}
}

void beolvas(int* v) {
	scanf("%d", v);
}

void kiir(int* v) {
	printf("%d\n", *v);
}

void duplaz(int* v) {
	(*v) *= 2;
}

int main()
{
	int t[N];
	bejar(t, beolvas);
	bejar(t, kiir);
	bejar(t, duplaz);
	bejar(t, kiir);
	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;
}




    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.

Írjuk ki a parancssorban lévő argumentumokat:


#include <stdio.h>

main(int argc, char **argv)
// 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]);
    }
}

Mentsük el a fenti programot arg.c néven és fordítsuk le!

$ gcc -o arg arg.c
$ ./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]);
    }

    printf("Az argumentumok osszege : %d\n", arg);
	
    return 0;
}


F: Írj egy programot, amely n-szer egymás után fűzi ugyanazt az s sztringet,
   ahol n és s is parancssori paraméter.

==============================================================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[])
{

    int i;
    char *er;

    if(argc < 3)
    {
        printf("Használat: %s <valami szám> <valami sztring>\n", argv[0]);
        return 1;
    }
    
    er = (char*) calloc ( atoi(argv[1]) + 1 , strlen(argv[2]) );

    strcpy(er,argv[2]);
    for(i=0;i<atoi(argv[1])-1;i++){
        strcat(er,argv[2]);
    }
    
    printf("%s\n",er);

    free(er);
    return 0;
}




Plusz pontos órai feladat

Írj akasztófa játékot (egyszerűsétett verzió). Adj megy egy kezdeti sztringet. Ezt kell majd kitalálni. Olvass be minden körben egy tipp karaktert. A játék addig menjen, amíg el nem fogy az élet (10), vagy ki nem találja a játékos a szót. A játék közben jelezzük a maradék életek számát, illetve hogy mennyit találtunk ki addig a szóból.

Egy lehetséges megoldás akasztofa.c .




    Jövő héten 8. miniZH (2016.11.21./22.)

Téma:

  3. - 10. gyakorlat anyaga.

  Gyakorlásra:

    A honlapomon a 3. - 10. gyakorlathoz tartozó anyag, magyarázatokkal, példákkal.

    A gyakorlatok végén lévő feladatok megoldása.

    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 50 percetek lesz a feladatok megoldására és beadására (tehát 15:50-ig/17:50-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, mivel a bíró rendszer nyit, majd automatikusan zár is. Hiányozni csak igazolással lehet, de a ZH akkor sem pótolható!


12. gyakorlat




    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;
}

Egy kis körítéssel:


#include <stdio.h>
#include <math.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("MIN(e^3, 3^2):\t%f\n", min( exp(3), pow(3, 2) ) );	// exp - math.h-ból jön - a paraméterben kapott szám a hatványkitevője e-nek
                                                                // pow - math.h-ból jön - a paramáterben kapott első számnak a hatványa.
                                                                //                      - a paraméterben kapott második szám a hatványkitevő
    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

Használat függvényen belül:


#include <stdio.h>
#include <math.h>

int main() {
    printf("MIN(e^3, 3^2):\t%f\n", ((exp(3))<(pow(3, 2))?(exp(3)):(pow(3, 2))) );
    return 0;
}

A zárójelek fontosságára az alábbi példa mutat rá:


#include <stdio.h>

#define MAX(a,b) (((a) > (b) ) ? (a) : (b))
#define MIN(a,b) (((a) > (b) ) ? (b) : (a))
#define MIN3(a,b,c) (((a) < (b)) ? MIN(a,c) : MIN(b,c))
/*
 * MINDENT zárójelezni kell, ugyanis ha így írnánk:
 * #define MAX(a,b) a > b ? a : b
 *
 * akkor MAX( a-3 , a?1:3 ) esetén az eredmény:
 * a-3 > a?1:3 ? a-3 : a?1:3 , ami nem az, amit szeretnénk!
 *
 */
int main()
{
    int a,b,c;
    a = -23;
    b = 44;
    c = 0;
    printf("MAX(%d,%d)=%d\n",a,b,MAX(a,b));
    printf("MIN(%d,%d)=%d\n",b,c,MIN(b,c));
    printf("MIN3(%d,%d,%d)=%d\n",a,b,c,MIN3(a,b,c));

    return 0;
}

Í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.




    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 -Wall -pedantic -c lib.c
$ gcc -Wall -pedantic -c libmain.c
$ gcc -Wall -pedantic -o lm lib.o libmain.o
vagy:
$ gcc -Wall -pedantic -o lm lib.c libmain.c


==============================================================================
== 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;
}




Jövő héten - 2016.11.28./29. - NagyZH!!!

Egész órán (135 perc). A Bírón előre kiadott feladatokból kap mindenki egyet. Sok sikert!



Nagy ZH!




Javító ZH!






C kód szintaxis kiemelő stílus csere