Programozás alapjai gyakorlat 2015-2016/1

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ás1
} else {
    utasítás2
}

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,out;
   for (i=1, j=100, out=0; i<=10; i++, j--)
      out+=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 házi 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!

Beküldési határidő: keddieknek 2015. 09. 25., éjfél, csütörtökieknek 2015. 09. 27., éjfél.

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



Házi feladat

A házi feladatot megoldani nem kötelező és bemutatni sem kell, viszont a következő gyakorlaton visszakérhető (kikérdezés, táblához hívás, stb. formájában)! Ha a hallgató megoldása ötletes, szép kivitelezésű, plusz pont adható. Amennyiben viszont nem tudja megoldani gyakorlaton a házi feladatban szereplő példákat vagy nem tud válaszolni az azzal kapcsolatban feltett kérdésekre, mínusz pont adható. Plusz és mínusz pontból is egyaránt maximum 10 pontot gyűjthet össze egy-egy hallgató.

A házi feladat és további gyakorló példák elérhetőek a PUB-ban ( /n/pub/ProgramozasAlapjai/Gyakorlat/gyak04/ )

Bárkinek bármi kérdése adódik a feladatokkal kapcsolatban írjon nyugodtan! ( Gyakoroljatok sokat! )




    Jövő héten 2. miniZH (2015.09.29./10.01.)

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 gyakorlatok végén lévő házi feladat és gyakorló feladatok megoldása.

    A honlapom mellet 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 8:40-ig/12: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ó!

     Vissza a lap tetejére.