A megoldások a 6. gyakorlat anyagánál elérhetőek, a feladatkiírások helyén.
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.
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ó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?
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;
}
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;
}
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 .
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 .
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! )
Vissza a lap tetejére.