Informatika2-2013/Osszefoglalas1 6

A MathWikiből
(Változatok közti eltérés)
344. sor: 344. sor:
 
</c>
 
</c>
  
Felrakta
+
Felraktam a megoldást és a pontok.txt-t: [http://math.bme.hu/~kkovacs/info2/konzult.c konzult.c], [http://math.bme.hu/~kkovacs/info2/pontok.txt pontok.txt]
  
 
== Gyakorlati feladatok megoldásai ==
 
== Gyakorlati feladatok megoldásai ==

A lap 2013. április 4., 23:53-kori változata


Menetrend

Elkezdtem a gyakorlatok feladatait kommentezve megoldani.

A tervezettbõl eddig kész: 60%

Várható befejezés: 01:45

1. gyakorlat

A gyakorlat elején gcc-vel fordítottunk egy "hello world" programot. Hasznos tudni, hogy hogyan lehet így terminálból fordítani egy C programot.

Majd megtanultuk a Codeblocks / Codelite használatát, ezekbõl nem igazán tudnánk mit visszakérdezni, ezeket a kényelem kedvéért mutattuk meg.

A gyakorlat végén pedig egy nagyon egyszerû kiegészítendõ feladatot oldottunk meg, aminek annyi volt a lényege, hogy lássátok hogyan kell deklarálni változókat (pl: int x;) és hogyan kell használni egyszerû elágazást.

2. gyakorlat

Megtanultuk hogyan lehet scanf-el beolvasni:

int z;
scanf("%d", &z);

Ekkor még nem tudtuk miért kell az & jel a z elé, de mostmár tudjuk, hogy ez azért kellett, hogy a változóba beleírhassa a felhasználó által beírt értéket. Emlékezzük vissza miért nem lehet ezt pointer nélkül megoldani:

void swap(int x, int y){
    int temp = x;
    x = y
    y = temp;
}
 
void swap2(int* x, int* y){
    int temp = *x;
    *x = *y
    *y = temp;
}
 
int main(void){
    int a = 6;
    int b = 8;
    swap(a, b);
    printf("%d %d", a, b); // 6 8-at ír ki
    swap2(&a, &b);
    printf("%d %d", a, b); // 8 6-ot ír ki
    return 0;
}

Itt az elsõ swap függvén igaz úgy tûnik hogy megcseréli a változók értékét, de valójában csak a róluk készült másolatok értéket cserélte meg, és így a main-ben nem történt csere. Viszont a swap2 pointereken keresztül jól megcseréli a valódi a és b változó értékét.

A gyakorlat része volt még a ciklusokkal való ismerkedés:

for(inicializálás; feltétel; inkrementálás){
    utasítások
}
 
while(feltétel){
    utasítások
}
 
do{
    utasítások
} while(feltétel);

A for ciklust sokszor használtuk, tömbökön való végigjáráshoz, leszámolásokhoz, mindenféléhez. while-t kevésbé, de végülis helyettesíthetõ egy for(;feltétel;) ciklussal. do while-t pedig mégkevésbé használtuk, ennek ugye a sajátossága, hogy amikor belép a ciklusba elõször akkor nem ellenõrzi a feltételt, tehát ha egy mindig hamis feltételünk van a ciklus magja akkor is lefut egyszer.

Ciklusokkal megkerestük folyamatosan érkezõ számoknak a minimumát vagy maximumát, ez azon az egyszerû ötleten alapult, hogy mindig mentettük az eddig talált legkisebb számot (minimum esetén) és ezzel hasonlítottuk össze az éppen érkezõt. Majd aszerint hogy kisebb volt az eddigi legkisebbnél a most vizsgált vagy sem, beállítottuk ezt a legkisebbnek, vagy csak mentünk tovább.

3. Gyakorlat

A gyakorlat elején megtudtuk, hogy a változóink végesek, így nem tárolhatunk bennük akármekkora számot.

Írtunk for ciklusokkal pár feladatot, majd áttértünk a gyakorlat anyagára, amik a típusok voltak.

Tömböket is megtanultunk használni, egy egyszerû példa:

#include<stdio.h>
 
int main(void) {
    int t[20];     // Itt hozzuk letre a tombot, elore megadjuk a meretet
    int i;
    t[5] = 5 * 5;             // Igy tudunk egy elemenek erteket adni
    t[6] = t[5] + 11;         // Vagy felhasznalni ertekkent
    for(i = 0; i < 20; i++) { // Ez a ciklus vegzi el a munkankat
        t[i] = i * i;
    }
    return 0;
}

4. Gyakorlat

A gyakorlat két nagyon fontos dolgot vezetett be, a függvényeket és a pointereket.

v_ért fv_név(p_típus1 p_név1, p_típus2 p_név2){
    /*  */
    return érték;
}

Függvényeknek definíciója a visszatérési értékükkel kezdõdik, majd a függvény neve és zárójelben a bemeneti paraméterek. Mint minden blokkal rendelkezõ szerkezetnél itt is kapcsos zárójellel kell elkezdeni és bezárni a blokkot, azaz a függvény magját. Ha a függvényben valahol return-ölünk egy értéket, a függvény visszatér, kilép, azaz a return utáni rész már nem fut le. Ezért az alábbi két függvény ekvivalens:

int fv(int a){
    if(a < 0){
        return -a;
    }
    else{
        return a;
    }
}
 
int fv2(int a){
    if(a < 0){
        return -a;
    }
    return a;   // Ez csak akkor fut le ha az if feltétele hamis volt, mert különben a függvény visszatér -a-val
}

Függvényeket lehet értékként is használni, így ha van egy átlag függvényünk, nem kell külön változóba elmenteni az általa visszaadott értéket:

printf("%lf", atlag(3.2, 6));

Pointerekrõl már írtam korábban, amit még megemlítenék, hogy a tömbök pointerek és a pointerek tömbök. Így ha t egy tömb vagy pointer, akkor t[0] és *t ekvivalens, továbbá, ha létezik i. indexû eleme, akkor t[i] és *(t+i) is ekvivalensek.

5. Gyakorlat

A gyakorlat elsõ felében dinamikus memóriakezelésrõl volt szó. Korábban csak fix méretû tömböket tudtunk létrehozni:

int t[20];

Ha dinamikusan foglalunk le memóriát akkor bármilyen pozitív egész változó méretû tömböt lefoglalhatunk:

int m;          // ebbe olvassuk be a tömb méretét
scanf("%d",&m);  
int *vec = (int *)malloc(m * sizeof(int)); // itt foglaljuk le a memóriát a tömbnek

Itt még megemlíteném, hogy igaz gyakorlatokon csak a malloc-ot használtuk, így a többit nem fogjuk kérdezni gyakorlati feladatokban. De elméleti feladatba bõven belefér pl, hogy mire való a calloc, realloc stb.

A gyakorlat másik felében raktár kezelõ függvényeket írtunk, ezeknek a megoldása megtalálható a megfelelõ házifeladat oldalán.

6. Gyakorlat

A gyakorlat 3 dologról szólt, file kezelés, argumentumok és struktúrák.

A file kezelést nem nehéz elsajátítani, ha a scanf és printf-ekkel már tisztában van az ember:

FILE* fp;            // Letrehozzuk a file pointerunket
fp = fopen("test.txt", "w");  // Megnyitjuk a test.txt-t irasra

Ezek után a sorok után használhatjuk az fprintf függvényt, hogy írjunk a test.txt file-ba. Ha olvasni szeretnénk egy file-ból akkor a "w" helyett "r"-et írjuk, ekkor az fscanf használható. Ha nem szeretnénk felülírni a file-t akkor használhatjuk az "a"-t, ami a file végére ír.

fprintf(fp, "Most irunk a file-ba.\n");

Ha "r"-el van megnyitva:

fscanf(fp, "%d", &z);         // Kiolvasunk a file-bol egy int-et

Az argumentum kezelésrõl a módosított main függvény fejét mindenképp érdemes megjegyezni:

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

Ha így definiáljuk a main függvényt, akkor argv[0]-ban a program neve lesz, argv[1]-tõl pedig a programnak adott argumentumok találhatók. Ha pl "./prog_neve teszt 20" paranccsal futtatjuk a prog_neve nevû programunkat, akkor az argv[1] a teszt string lesz, az argv[2] pedig a 20 string, tehát nem a 20 szám, hogy számként használhassuk az atoi függvényt kell rá használnunk:

int m = atoi(argv[2]);

Az argc-ben amit igazából nem használtunk az argumentumok száma szerepel, szóval az elõzõ példában pl 2.

A gyakorlat vége struktúrákról és a typedef-rõl szólt.

typedef int egesz;

Ezután a sor után használhatjuk az egesz kifejezést bárhol int helyett, pl:

egesz i;
egesz n;

Struktúrák több változót zárnak egységbe, például létrehozhatunk egy dátum változót, amiben tároljuk az évet, hónapot és napot:

struct Datum{
    int ev;
    int honap;
    int nap;
};

Ezután a következõ képpen hozhatunk létre egy ilyen változót és állíthatjuk be az adattagjait:

struct Datum d1;
d1.ev = 2013;
d1.honap = 4;
d1.nap = 4;

Amint látható a struct kulcsszót is ki kell írni mikor deklarálunk egy változót, ezt nem igazán szeretjük, így typedef-el szoktuk "megjavítani", az elõzõ struktúra definíció helyett:

typedef struct{
    int ev;
    int honap;
    int nap;
} Datum;

Ezután létrehozható Datum változó és ugyanúgy mûködik mint korábban:

Datum d1;
d1.ev = 2013;
d1.honap = 4;
d1.nap = 4;

Ami a typedef-nél történik, hogy azt az egész struktúrát nevezzük el Datum-nak, pont mint amikor az int-et neveztük el egesz-nek.

A gyakorlat végén egy Vektor struktúrával dolgoztunk, írtunk hozzá függvényeket, a házi is errõl szólt, így a kapcsolódó házi oldalán megtalálható pár függvény megoldása.


Konzultáció feladat

A feladatnak nem az volt a célja, hogy egy átlagos ZH feladatot mutasson be, hanem hogy szinte minden szerepeljen benne ami a gyakokon szerepelt, így jól tudjunk magyarázni a segítségével.

A feladat

Írjunk programot, melynek 2 argumentuma van, egy file név és egy szám. Az adott file-ban soronként két egész szám található, pontok a síkon, az adott szám pedig azt határozza meg hogy hány darab ilyen pont található a file-ben.

Hozzunk létre egy struktúrát amiben tárolni tudunk egy ilyen pontot. Majd olvassuk ki a file-ból mindet egy dinamikusan foglalt tömbbe. Végül írjuk ki azokat a pontokat amiknek a koordinátáik összege fibonacci szám.

Megoldás tervezése

A struktúránknak két egészet kell tárolnia. Érdemes lenne megírni egy olyan függvényt mely eldönti egy adott egész számról, hogy fibonacci-e vagy sem, majd ezt felhasználni a vizsgálatkor. A dinamikus tömb foglalásnál az általunk létrehozott stuktúrákat tároló tömböt kell lefoglalnunk, ez semmiben nem különbözik a beépített típusok lefoglalásától.

Elõször írjuk meg a fibonacci vizsgáló függvényt. Majd kezeljük le az argumentumokat, nyissuk meg a file-t. Végül olvassuk ki a file-ból a pontokat és nézzük meg hogy a koordinátáik összege fibonacci-e.

A megoldás

#include<stdio.h>
#include<stdlib.h>
 
// A függvény 0-t fog adni ha nem fibonacci az adott szám, és 1-et ha az. C-ben 0 a hamis és 1 az igaznak felel meg, if-ben is használhatók.
int fibonacci_e(int n){
    int x = 1; // 0.
    int y = 1; // és 1. fibonacci szám, y-ban lesz mindig a nagyobb, x-ben az elõzõ fibonacci szám
    if(n == 0 || n == 1) // Úgy döntöttünk, hogy a 0 és 1 fibonacci, így egyet adunk vissza rögtön ha valamelyiket kapjuk
        return 1;   // Itt igaz nem írtam {}-eket, de nem is kell, ha nem teszem ki, akkor az if az utána következõ parancsra vonatkozik csak
 
    while(y < n){   // Megkeressük azt a fibonacci számot ami egyenlõ vagy nagyobb a kapott számunknál
        int temp = y;   // Elmentem az y értéket, így tudom csak megoldani, hogy majd x-nek értékül adhassam
        y = y + x;      // F_{i+1} = F_{i} + F_{i-1}
        x = temp;       // F_{i}
    }
 
    if(y == n)          // Ha azért álltunk meg mert egyenlõ volt a kapott szám egy fibonacci-val
        return 1;       // Akkor igazzal térünk vissza
    else                // Különben hamissal
        return 0;       // Itt ugye az else maga nem is kellett volna, lehetett volna írni a return 0-t simán a függvény végére
                        // mert ha y == n igaz volt, akkor úgyis kilép a függvény, szóval a return 0 csak akkor futna le ha az nem volt igaz
}
 
// A struktúránk 2 int-et tárol:
typedef struct{
    int x;
    int y;
} Pont;
 
// Argumentumos main-t kell írnunk mert argumentumként kapunk két dolgot is
int main(int argc, char* argv[]){
    FILE* fp;   // Ebbe nyitjuk majd meg a file-t
    int i;      // Ciklusváltozó
    int n;      // Ebbe mentjük a pontok darabszámát (2. argumentum)
 
    fp = fopen(argv[1], "r");   // Megnyitjuk az elsõ argumentumként kapott számot olvasásra
 
    n = atoi(argv[2]);      // A második argumentumként kapott számot elõbb string-bõl int-é kell alakítanunk az atoi függvénnyel, hogy használhassuk
 
    // Foglaljuk le a tömböt a pontoknak
    Pont* t = (Pont*)malloc(n * sizeof(Pont));  // Ahogy mondtam a lefoglalás nem különbözik a korábbiaktól, csak nem int, double, stb-t írunk, hanem Pont-ot.
 
    // Olvassuk be a file-ból a pontokat:
    for(i = 0; i < n; i++){             // n-ig megyünk mivel n darab pontunk van
        fscanf(fp, "%d", &(t[i].x));    // Elõször az x koordinátát olvassuk be. t[i] az i. pontunk, t[i].x az i. pont x koordinátája, &(t[i].x) ennek a pointere, hogy ebbe olvassa be a számot
        fscanf(fp, "%d", &(t[i].y));    // Aztán ugyanígy beolvassuk az y koordinátát is
    }
 
    // Egy külön ciklusban megnézzük hogy melyik pontok koordinátáinak az összege fibonacci és ezeket kiírjuk
    for(i = 0; i < n; i++){
        if(fibonacci_e(t[i].x + t[i].y)){       // A függvény vizsgálja a számokról hogy fibonaccik-e, t[i].x + t[i].y az i. pont koordinátáinak az összege
            printf("(%d, %d)\n", t[i].x, t[i].y);   // A kiírás pl így fog kinézni: (3, 2)
        }
    }
 
    return 0;       // return 0 ahogy a main végén szokásos
}

Példa futtatás linux-on, terminálból: ./program pontok.txt 10

Ahol a pontok.txt pl a következõ:

6 7
4 5
3 5
7 4
7 3
1 1
4 6
7 3
23 5
75 2

Felraktam a megoldást és a pontok.txt-t: konzult.c, pontok.txt

Gyakorlati feladatok megoldásai

Személyes eszközök