Programozás alapjai gyakorlat 2015-2016/1

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


Kötelező házi feladat

Az elvárt program megírásának lépései:

Hozzunk létre egy konstanst, amelyben egy tömbméretet fogunk tárolni. Legyen ez a konstans N. Az N értékét tetszőlegesen választhatjuk meg.

A main függvényben hozzunk létre egy N*N-es, 2 dimenziós, egész értékeket tároló tömböt. Ezt a tömböt töltsük fel. A tömb első elemének értéke legyen N. A többi elem sorban eggyel nagyobb értéket vegyen fel. Ehhez mentsük ki az N értékét először egy segédváltozóba, és azt állítsuk be a tömb első elemének, majd minden ciklusban növeljük eggyel az értékét. Ezt a műveletet elvégezheted a main függvényen belül. Pl.: ha N = 7, akkor a tömbünk:

 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 32 33 34
35 36 37 38 39 40 41
42 43 44 45 46 47 48
49 50 51 52 53 54 55

Ezután írassuk ki a tömbünket. A tömb kiíratásához, hozz létre egy kiiro nevű függvényt, mely típusa void legyen és egyetlen bemeneti paramétere a kiírandó tömb. A függvény fejléce az alábbi legyen tehát: void kiiro (int tomb[N][N]). Tehát ez a függvény kerül meghívásra a main jelenlegi pontján.

Ezután kérjünk be a felhasználótól egy 1 és 10 közötti egész számot, úgyszintén a main-en belül. Ügyeljünk rá, hogy ha a felhasználó nem ezen tartományba eső számot ad meg, akkor kérjük be újra. Pl.:

Add meg mely szammal oszthato ertekeket allitsuk 0-ra (1-10): 8

SZERK.: A szám beolvasása után haladjunk végig ismét a tömbünkön és nézzük meg, hogy az adott elem osztható-e maradék nélkül az előzőleg bekért számmal. Ha osztható, akkor cseréljük le az adott elemet 0-ra, ellenkező esetben ne tegyünk semmit. Ezt a műveletet egy csere nevű függvényben valósítsuk meg, melynek a típusa void legyen, kettő darab bemeneti paramétere pedig maga a tömb, illetve a cserélendő érték. (Hasonló fejléccel, mint a kiiro függvény esetén.)

Végezetül a kiiro függvény segítségével írjuk ki a végső tömbünket is.

A fenti beolvasott 8-as példa esetén a végső tömb kinézete:

 7  0  9 10 11 12 13
14 15  0 17 18 19 20
21 22 23  0 25 26 27
28 29 30 31  0 33 34
35 36 37 38 39  0 41
42 43 44 45 46 47  0
49 50 51 52 53 54 55

A program megírásához szügséges tananyag mind megtalálható a webolalamon és példát is néztünk minden fentebb említett műveletre.

A cél az lenne, hogy a feladat megoldása közben áttanulmányozzátok az eddig tanultakat, és tényleges programozással gyakorolnátok azokat.

A kötelező házi feladat megoldására nem jár plusz pont, viszont nem megoldása -1 pontot von maga után.

Felmerülő kérdések esetén természetesen email-ben lehet kérdezni, valamint a jövő heti konzultációs órán személyesen.

SZERK.: Beküldési határidő: keddieknek és csütörtökieknek is egyaránt 2015. 10. 26., éjfél (tehát kb. másfél hét).

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

Egy lehetséges megoldás letöltése .



Plusz pontos házi feladat

Megvalósítandó játék: BLACK JACK (21-ezés)

Kiírás: Egy játékos játszik a számítógép ellen. A játékot francia kártyán játszák, tehát a bejövő lapok értéke 1..11 lehet. Kezdetben mindkét játékosnak 0 lapja van. A két játékos ezután felváltva kérhet lapot. A játékostól kérjük be, hogy szeretne-e még lapot, a számítógépnek pedig legyen beállítva egy felső korlát, hogy milyen lapépérték összegre húzzon még rá. Figyeljünk rá oda, hogy attól, hogy esetleg a játékos már nem kér több lapot, attól még a számítógép kapjon, ha nem érte még el a felső korlátot. Miután mindkét játékos jelezte, hogy már nem kér több lapot, hasonlítsuk össze a lapértékek összegét, majd a Black Jack szabályai alapján hirdessünk nyertest.

21-ezésről bővebben: itt .

Egy lehetséges futási eredmény:

<<< BLACK JACK >>>

Kersz-e lapot (1 - igen, 0 - nem)? 1
Lapjaid osszege eddig: 9

Kersz-e lapot (1 - igen, 0 - nem)? 1
Lapjaid osszege eddig: 12

Kersz-e lapot (1 - igen, 0 - nem)? 1
Lapjaid osszege eddig: 21

Kersz-e lapot (1 - igen, 0 - nem)? 0

Te pontszamod: 21
Ellenfel pontszama: 22

Nyertel!

A te lapjaid rendre:
9 3 9

Az ellenfel lapjai rendre:
3 7 5 7

Felmerülő kérdések esetén természetesen email-ben lehet kérdezni, valamint a jövő heti konzultációs órán személyesen.

Beküldési határidő: keddieknek 2015. 10. 21., éjfél, csütörtökieknek 2015. 10. 23., éjfél.

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

Egy lehetséges megoldás letöltése .




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/gyak07/ )

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




    Jövő héten nincs miniZH (2015.10.20./22.)


     Vissza a lap tetejére.