Labor, 6. hét: struktúrák és függvények

Pohl László, Czirkos Zoltán · 2016.08.28.

Struktúrák és függvények kezelése. Néhány vezérlési szerkezet.

Felkészülés a laborra:

1. Haladási napló

Töltsd ki a haladási naplódat a linkre kattintva: /adminhallgato?haladasinaplo. A régiekre adott válaszokat is frissítsd, ha úgy gondolod, megtanultál már egy adott témakört!

2. Függvények – alapok

Írj függvényeket, amelyek valós számot vesznek át, és visszatérnek az:

  • kob() – harmadik hatványával,
  • abszolut() – abszolút értékével (van fabs() függvény, de most ne használd)!

Írj programot, amelyik a = −1-től +1-ig, tizedenként lépve, kiírja egymás mellé a, a3, |a| és sin(a) értékét, mindig négy tizedesjegy pontossággal!

Megoldás

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

double kob(double x) {
    return x*x*x;
    /* vagy: return pow(x, 3); */
}

double abszolut(double x) {
    if (x < 0)
        return -x;
    else
        return x;
    /* vagy: return x < 0 ? -x : x; */
}

int main(void) {
    double a;
    for (a = -1; a <= +1; a += 0.1) {
        printf("%10.4f %10.4f %10.4f %10.4f\n", a, kob(a), abszolut(a), sin(a));
    }

    return 0;
}

3. Madárnyelv

Adott az alábbi program, amely madárnyelven (mavadávárnyevelveven) írja ki a beírt szöveget.

#include <stdio.h>

int main(void) {
    char c;
    while (scanf("%c", &c) != EOF) {
        if (c=='a' || c=='e' || c=='i' || c=='o' || c=='u')
            printf("%cv%c", c, c);
        else
            printf("%c", c);
    }

    return 0;
}

Írj függvényt, amelyik megmondja egy betűről, hogy magánhangzó-e! Alakítsd át úgy a programot, hogy a megírt függvényt használod a main()-ben!

Megoldás

#include <stdio.h>
#include <stdbool.h>

/* Igaz ertekkel ter vissza, ha a parametere egy maganhangzo. */
bool maganhangzo(char c) {
    return c=='a' || c=='e' || c=='i' || c=='o' || c=='u';
}

int main(void) {
    char c;
    while (scanf("%c", &c) != EOF)
        if (maganhangzo(c))
            printf("%cv%c", c, c);
        else
            printf("%c", c);

    return 0;
}

Hogyan lehetne megoldani azt, hogy a nagybetűvel kezdődő szavakat is helyesen kezelje a program? Pl. az „Alma” szóra azt kell kiírnia, hogy „Avalmava”. Ehhez fel kell tudnia ismerni a nagybetűvel írt magánhangzókat is. Kiíráskor a v betű előtt az eredeti karaktert kell kiírni, utána pedig a kisbetűsítettet. Használhatod a meglévő maganhangzo() függvényt is, csak a beolvasott karaktert kisbetűsítve kell odaadnod neki.

Megoldás

Jól látszik a megoldásban a fenti magyarázat: a maganhangzo függvénynek a kisbetűsített karaktert adjuk: maganhangzo(tolower(c)). A kiírásnál az első karakter az eredeti: c, a második a kisbetűsített: tolower(c). Használható a beépített tolower() függvény is (#include <ctype.h>), vagy egy saját változat.

while (scanf("%c", &c)==1)
    if (maganhangzo(tolower(c)))
        printf("%cv%c", c, tolower(c));
    else
        printf("%c", c);

4. Összetett adatszerkezet

Az itt látható táblázat egy futóverseny eredményeit tartalmazza.

IndexNévSzületésHelyezés
0Am Erika1984. 05. 06.1
1Break Elek1982. 09. 30.3
2Dil Emma1988. 08. 25.2
3Kasza Blanka1979. 06. 10.5
4Reset Elek1992. 04. 05.4

Alább egy elkezdett programot látsz, amelyben a megfelelő típusok már definiálva vannak, és az adatokat egy tömb tartalmazza. Egészítsd ki a programot, hogy kiírja a képernyőre a kommentekben megadott adatokat!

#include <stdio.h>

typedef struct Datum {
    int ev, ho, nap;
} Datum;

typedef struct Versenyzo {
    char nev[31];
    Datum szuletes;
    int helyezes;
} Versenyzo;

void datum_kiir(Datum d);

void versenyzo_kiir(Versenyzo v);

int main() {
    Versenyzo versenyzok[5] = {
        { "Am Erika", {1984, 5, 6}, 1 },
        { "Break Elek", {1982, 9, 30}, 3 },
        { "Dil Emma", {1988, 8, 25}, 2 },
        { "Kasza Blanka", {1979, 6, 10}, 5 },
        { "Reset Elek", {1992, 4, 5}, 4 },
    };

    /* 0-s versenyző neve - printf %s */
    /* 2-es versenyző helyezése */
    /* 4-es versenyző születési dátumát a megadott függvénnyel */
    /* 1-es versenyző nevének kezdőbetűjét (ne feledd, a sztring karaktertömb) */
    /* az 1-es versenyző dobogós-e? igen/nem, akár ?: operátorral */
    /* az 4-es versenyző gyorsabb-e, mint a 3-as versenyző? */
    /* az 1-es versenyző ugyanabban az évben született-e, mint a 2-es? */
    /* egészítsd ki a versenyzo_kiir() függvényt,
     * aztán írd ki az 1-es versenyző összes adatát */
    /* végül listázd ki az összes versenyzőt sorszámozva, összes adatukkal. */

    return 0;
}

void datum_kiir(Datum d) {
    printf("%d.%d.%d.\n", d.ev, d.ho, d.nap);
}

void versenyzo_kiir(Versenyzo v) {
    /* a versenyző összes adatának kiírása */
}

Megoldás

#include <stdio.h>

typedef struct Datum {
    int ev, ho, nap;
} Datum;

typedef struct Versenyzo {
    char nev[31];
    Datum szuletes;
    int helyezes;
} Versenyzo;

void datum_kiir(Datum d);

void versenyzo_kiir(Versenyzo v);

int main() {
    Versenyzo versenyzok[5] = {
        { "Am Erika", {1984, 5, 6}, 1 },
        { "Break Elek", {1982, 9, 30}, 3 },
        { "Dil Emma", {1988, 8, 25}, 2 },
        { "Kasza Blanka", {1979, 6, 10}, 5 },
        { "Reset Elek", {1992, 4, 5}, 4 },
    };

    /* 0-s versenyző neve - printf %s */
    printf("%s\n", versenyzok[0].nev);
    /* 2-es versenyző helyezése */
    printf("%d\n", versenyzok[2].helyezes);
    /* 4-es versenyző születési dátumát a megadott függvénnyel */
    datum_kiir(versenyzok[4].szuletes);
    /* 1-es versenyző nevének kezdőbetűjét (ne feledd, a sztring karaktertömb) */
    printf("%c\n", versenyzok[1].nev[0]);
    /* az 1-es versenyző dobogós-e? igen/nem, akár ?: operátorral */
    printf("%s\n", versenyzok[1].helyezes <= 3 ? "igen" : "nem");
    /* az 4-es versenyző gyorsabb-e, mint a 3-as versenyző? */
    printf("%s\n", versenyzok[4].helyezes < versenyzok[3].helyezes ? "igen" : "nem");
    /* az 1-es versenyző ugyanabban az évben született-e, mint a 2-es? */
    printf("%s\n", versenyzok[1].szuletes.ev == versenyzok[2].szuletes.ev ? "igen" : "nem");
    /* egészítsd ki a versenyzo_kiir() függvényt,
     * aztán írd ki az 1-es versenyző összes adatát */
    versenyzo_kiir(versenyzok[1]);
    /* végül listázd ki az összes versenyzőt sorszámozva, összes adatukkal. */
    int i;
    for (i = 0; i < 5; ++i) {
        printf("%d. ", i);
        versenyzo_kiir(versenyzok[i]);
    }

    return 0;
}

void datum_kiir(Datum d) {
    printf("%d.%d.%d.\n", d.ev, d.ho, d.nap);
}

void versenyzo_kiir(Versenyzo v) {
    printf("%s, %d.%d.%d., %d\n", v.nev, v.szuletes.ev, v.szuletes.ho, v.szuletes.nap, v.helyezes);
}

5. Menüvezérelt program

Készíts egyszerű menüvezérelt programot! A program tároljon el egy számot, melynek kezdőértéke a = 1. Ezt követően a program jelenítse meg a képernyőn a értékét, és az alább látható menüt. A megfelelő menüpont számának megadása (scanf()-el) után hajtsa végre a-n a kiválasztott műveletet, írja ki újból a új értékét és a menüt! A menüből mindaddig lehessen újból választani, míg a kilépést nem választja a felhasználó! Használj switch()-et és do ... while(), azaz hátultesztelő ciklust!

printf("0. Alapertek visszaallitasa (a = 1)\n"
       "1. Hozzaad 1-et\n"
       "2. Megforditja az elojelet\n"
       "3. Szorozza 2-vel\n"
       "9. Kilepes\n");

Minden egyes tevékenységet (műveletet) egy pici függvény valósítson meg, amelynek bemenő paramétere az a változó tartalma, visszatérési értéke pedig a megváltozott szám! A main() ezen függvények hívásával végezze el a feladatát!

Miért olyan lényeges ez a feladat?

Figyeld meg a kapott főprogramot! Ez irányítja a többi függvény működését: meghívja az egyes részfeladatokhoz tartozó alprogramokat, amelyek dolgukat végezve visszatérnek, újra a főprogram kezébe adva az irányítást. A főprogram és az alprogramok a paramétereken és a visszatérési értékeken keresztül kommunikálnak.

Megoldás

A tevékenység kiválasztásához a switch() szerkezetet érdemes használni; úgy egymás alatt, szám szerint felsorolhatóak az értékadások. Figyelni kell, ne maradjon ki a break! A menürendszer keretét pedig egy ciklus adja. Ez azért lehet hátultesztelő, mert egyszer biztosan végre kell hajtani a törzsét: a menü kiírását, a szám beolvasását.

#include <stdio.h>

int alap() {
    return 1;
}

int novel(int a) {
    return a+1;
}

int megfordit(int a) {
    return -a;
}

int duplaz(int a) {
    return 2*a;
}

int main(void) {
    int menupont;
    int a = alap();
    do {
        printf("a = %d\n\n", a);
        printf(
            "0. Alapérték visszaállítása (a = 1)\n"
            "1. Hozzáad 1-et\n"
            "2. Megfordítja az előjelét\n"
            "3. Szorozza 2-vel\n"
            "9. Kilépés\n"
            "? ");
        scanf("%d", &menupont);
        switch (menupont) {
            case 0: a = alap(); break;
            case 1: a = novel(a); break;
            case 2: a = megfordit(a); break;
            case 3: a = duplaz(a); break;
            case 9: /* semmi, majd kilepunk */ break;
            default: printf("NA!\n"); break;
        }
        printf("\n");          /* hogy ne folyjon ossze */
    } while (menupont != 9);

    return 0;
}

6. Kerítés

Adj meg egy Pont típust, amely kétdimenziós koordinátát (x, y) tárol! Írj függvényt (tav()), amely a paraméterként kapott két pont távolságával tér vissza! (Ehhez Pitagorasz tételét kell használni.) Definiálj két pontot a forráskódban! Számíttasd ki ezek távolságát, és írd ki a képernyőre!

Megoldás

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

typedef struct Pont {
    double x, y;
} Pont;

double tav(Pont p1, Pont p2) {
    return sqrt(pow(p1.x-p2.x, 2) + pow(p1.y-p2.y, 2));
}

int main(void) {
    Pont p1 = { 2.1, 3.2 };
    Pont p2 = { 4.7, -1.4 };

    printf("%g\n", tav(p1, p2));

    return 0;
}

Ha kész, írj további függvényeket:

  • Olyat, amelyik megvizsgál két pontot, és megmondja, hogy egybeesnek-e!
  • Olyat, amelyik beolvassa egy pont koordinátáit a billentyűzetről, és visszatér vele!

Ha ezek is megvannak, az eddigiek használatával oldd meg az alábbi feladatot:

Egy gazda szeretné körbekeríteni a telkét drótkerítéssel. Írj programot, amely kiszámítja, hogy mennyi kerítésre lesz szüksége! A program kérje egymás után a kerítésoszlopok koordinátáit (x, y koordinátapárok), számítsa ki az aktuális és az előző oszlop távolságát, és összegezze a távolságokat! Az összegzést addig folytassa, amíg a megadott koordináták nem egyeznek az elsőként megadott koordinátapárral, vagyis míg vissza nem ér a kezdőoszlophoz!

Ehhez célszerű egy változóba följegyezni a kezdőpontot, azután pedig két további, pont típusú változóval dolgozni: az egyik tárolja az új pont adatait, a másik pedig mindig az eggyel előzőt.

Megoldás

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

typedef struct Pont {
    double x, y;
} Pont;

double tav(Pont p1, Pont p2) {
    return sqrt(pow(p1.x-p2.x, 2) + pow(p1.y-p2.y, 2));
}

bool egyenlo(Pont p1, Pont p2) {
    return p1.x == p2.x && p1.y == p2.y;
}

Pont beolvas(void) {
    Pont p;
    scanf("%lf%lf", &p.x, &p.y);
    return p;
}
 
int main(void) {
    /* A kezdőpont, ehhez kell majd visszatérni */
    printf("Kezdőpont:\n");
    Pont k;
    k = beolvas();

    /* Összegzés */
    double hossz = 0;
    printf("További pontok:\n");
    Pont p_elozo;  /* mindig ez lesz az előző pont */
    Pont p_uj;  /* meg amit most beolvastunk */
    p_elozo = k;
    do {
        p_uj = beolvas();
        hossz += tav(p_elozo, p_uj);
        p_elozo = p_uj;        /* a következő iteráció számára */
    } while (!egyenlo(p_uj, k));
 
    /* Összeg kiírása */
    printf("A hossz: %f\n", hossz);
 
    return 0;
}

7. További feladatok

Ha elkészültél, folytasd a feladatgyűjtemény ehhez a témakörhöz kapcsolódó struktúrákkal kapcsolatos feladataival!