Labor, 2. hét: vezérlési szerkezetek

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

Az integrált fejlesztőkörnyezet használata. Vezérlési szerkezetek: elágazásokkal és ciklusokkal megoldható feladatok. Nyomkövetés és hibakeresés a Code::Blocks eszközeivel.

Ezen a héten a vezérlési szerkezeteket gyakoroljuk: elágazásokat és ciklusokat kell írni a feladatok megoldásához.

Felkészülés a laborra:

1. Haladási napló

Töltsd ki a haladási naplódat a linkre kattintva: /admin.

Itt minden héten lesznek új kérdések, amik segítik a felkészülést. De a régiekre adott válaszokat is frissíteni tudod, amikor úgy érzed, megtanultál már egy adott témakört!

2. Hibák a programban

Az alábbi programkód egy csomó hibát tartalmaz. Másold be a fejlesztőkörnyezetbe a helló világ helyett!

#include <studio.h>;

int main{} {
    int x;

    printf("Kedves felhasznalo!");
    printf("Irj be egy szamot, es en kiirom a negyzetet!");
    scanf("%d", x);
    x = x*x
    printf("x negyzete: %f", x);
}

Próbáld meg lefordítani (Build/Build menüpont), és figyeld meg a fejlesztőkörnyezet hibaüzeneteit! Egy hibaüzenetre kattintva a szerkesztőben a kurzor a hivatkozott sorra ugrik. Haladj mindig az első hibaüzenettől, figyeld meg mindig az üzenet szövegét! A fejlesztőkörnyezet által jelzett hibákon kívül a megjelenés értelemzavaró hibáit is javítsd.

Érdemes az összes hibaüzenetet megszüntetni, a figyelmeztetéseket is, nem csak a hibákat! A hibaüzenetekből kétféle van:

Hiba (error)
Ezek olyan nyelvtani hibák, amelyek miatt egyáltalán nem fordítható le a program.
Figyelmeztetés (warning)
Ezek nem C nyelvtani hibák. A program ugyan lefordítható, de a fordítóprogram gyanúsnak, szokatlannak, valószínűsíthetően hibásnak ítéli őket. A figyelmeztetésekkesel leforduló programok legtöbbször nem működnek helyesen.

Megoldás

#include <stdio.h>

int main(void) {
    int x;

    printf("Kedves felhasznalo!\n");
    printf("Irj be egy szamot, es en kiirom a negyzetet!\n");
    scanf("%d", &x);
    x = x*x;
    printf("x negyzete: %d", x);

    return 0;
}

3. Szakasz hossza

Írj programot, amely a felhasználótól bekéri két síkbeli pont x és y koordinátáit, és kiírja a közéjük húzott egyenes szakasz hosszát (Pitagorasz tételével)! Fontold meg, hogy milyen adattípusokat kell használni!

A művelethez szükség lesz gyökvonásra, amelyhez a C nyelv matematikai könyvtárában található sqrt() függvény használható. Ez a math.h-ban van megadva. Tehát a program elejére a következő sort kell beírni:

#include <math.h>

Megjegyzés: aki Linuxon, parancssorból fordítja a programot, hozzá kell linkelnie a matematikai könyvtárat a programhoz a -lm kapcsolóval.

szakaszhossz
(0;0)–(1;1)1.414214
(1;5)–(4;1)5
(-3;2)–(5;7)9.433981

Megoldás

#include <stdio.h>
#include <math.h>  /* Gyökvonás miatt */

int main(void) {
    /* Beolvasások */
    double x1, y1, x2, y2;  /* Koordináták tárolása */
    printf("x1=");
    scanf("%lf", &x1);
    printf("y1=");
    scanf("%lf", &y1);
    printf("x2=");
    scanf("%lf", &x2);
    printf("y2=");
    scanf("%lf", &y2);

    /* Hosszúság kiszámítása */
    double hossz = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
    printf("A szakasz hossza: %f", hossz);

    return 0;
}

4. Másodfokú egyenlet

Írj programot, amely az ax2+bx+c=0 másodfokú egyenlet együtthatóit kérdezi a felhasználótól, és kiírja az egyenlet x1 és x2 megoldását! A megoldóképlet:

Próbáld ki a bal oldalt látható egyenletekre! Ezekkel ellenőrizni tudod a megoldásod. Próbáld ki aztán a jobb oldali egyenletekre is. Mit tapasztalsz? Miért?

egyenletmegoldás
2x2-x-6=0x1=2, x2=-1.5
x2-12x+35=0x1=5, x2=7
egyenletmegoldás
x2-2x+1=0?
x2+2x+10=0?

Írd át úgy a programot, hogy figyelembe vegye azokat az eseteket, amikor nincs, vagy csak egy valós gyök van, és eszerint végezd a kiírást!

Megoldás

Arra kell figyelni, hogy ne vonj negatív számból négyzetgyököt. A gyökvonás előtt a diszkrimináns vizsgálatával meg kell nézni, hány valós megoldás lesz.

Itt egy nagyobb C kifejezést kapsz, amiben többféle operátor (összeadás, kivonás, szorzás stb.) szerepel. Ezeknek a műveleteknek C-ben ugyanúgy van precedenciája, mint a matematikában. A szorzás pl. magasabb rendű művelet, mint az összeadás. A C nyelv precedencia szabályai összetettebbek; erről majd előadáson lesz szó.

Az if (diszkr==0) vizsgálat elméletben helyes, gyakorlatban azonban nem előnyös ilyet írni. A kerekítési hibák miatt előfordulhat az, hogy 0-hoz olyan közeli szám adódik, amit már 0-ra kerekít a számítógép. Erről is később lesz szó előadáson.

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

int main(void) {
    double a, b, c;
    printf("a=");
    scanf("%lf", &a);
    printf("b=");
    scanf("%lf", &b);
    printf("c=");
    scanf("%lf", &c);

    /* a diszkriminans: b² - 4ac */
    double diszkr;
    diszkr = b * b - 4 * a * c;

    /* ha <0, 2 komplex gyok lenne */
    if (diszkr < 0) {
        printf("Nincs valos gyok!\n");
    }
    /* ha pont 0, akkor 1 megoldas van */
    if (diszkr == 0) {
        printf("Egy valos gyok: %f\n", -b / (2 * a));
    }
    /* >0, akkor 2 megoldas */
    if (diszkr > 0) {
        printf("Ket gyok: %f, %f\n",
               (-b + sqrt(diszkr)) / (2 * a),
               (-b - sqrt(diszkr)) / (2 * a));
    }

    return 0;
}

5. Ciklusok: számok kiírása

Az ábrán egy program pszeudokódja látható, amely kiírja a számokat 1-től 20-ig.

Gondolok egy számra, legyen ez 1.
Ismétlés, amíg a szám ≤ 20
    Leírom a számot.
    Új sort kezdek.
    Növelem a számot 1-gyel.
Ismétlés eddig

C kód

Írd meg ezt a programot C-ben while() ciklussal!

Figyeld meg: ahogy gépeled be a while() utáni { kapcsos zárójelet, az Enter billentyű hatására a gép egyből néhány szóközzel beljebb kezdi a sorokat. A bezáró } kapcsos után pedig újra kintebb. Használd ezt ki! Így áttekinthetőbb lesz a programod, jobban látszik a ciklus belseje. Ha elrontottad a formázást, a Code::Blocks automatikusan, utólag is képes rendbe tenni a kódot: ehhez kattints jobb gombbal a forráskódra, és válaszd az előugró menüből a „Format use Astyle” menüpontot.

Nyomkövetés

Próbáld ki a nyomkövető használatával is! A Code::Blocksban a nyomkövetést legegyszerűbben úgy tudod elindítani, ha arra a sorra állsz a kurzorral, ahol először meg szeretnéd a programot állítani, és megnyomod az F4-et (Debug/Run to cursor). Innentől a program az F7-tel léptethető soronként (Debug/Next line). A „Debug/Debug windows/Watches” menüponttal hívhatod elő az ablakot, amelyben a változók (Local variables) értékét tudod figyelni.

Ne feledd, hogy a ciklusfeltétel azt mondja meg, hogy meddig ismételjük a műveleteket – amíg a feltétel igaz, addig újból és újból végrehajtja a ciklusmagban lévő utasításokat. Amikor hamissá válik, a ciklusmag utáni utasítással folytatja a végrehajtást. Figyeld a „Watches” ablakban a ciklusváltozó értékét! Mennyi az értéke a ciklus vége után? Miért jött ki a gép a ciklusból?

Írd át a while() ciklust for() ciklusra! Végezd el így is a nyomkövetést!

Határok megadása

Szám beolvasása. Alakítsd át az előző programot úgy, hogy ne 1 és 20 között írja ki a számokat, hanem a felhasználó által megadott határok között! A program indítása után ne csak villogjon a kurzor egy üres ablakban, hanem írja is ki a program, hogy épp mit kérdez, azaz milyen bemenetre vár!

Megoldás

#include <stdio.h>

int main(void) {
    /* Beolvasás */
    int mettol, meddig;
    printf("Mettol? ");
    scanf("%d", &mettol);
    printf("Meddig? ");
    scanf("%d", &meddig);

    /* Az "ettől-eddig-így" ciklusok szebbek,
     * áttekinthetőbbek a for (...) változatban */
    int i;
    for (i = mettol; i <= meddig; i = i+1)
        printf("%d\n", i); /* Szám kiírása */

    return 0;
}

Fordított intervallum. Próbáld ki, mi történik akkor, ha az előbbi programnak fordítva adja meg a felhasználó az intervallum határait (pl. 1–20 helyett 20–1). Nézd meg nyomkövetővel, mi történik! Egészítsd ki úgy a programot, hogy ilyenkor is helyesen működjön! Ehhez meg kell vizsgálnod, a két beolvasott szám közül melyik a nagyobb, és aszerint beállítanod két új, mettol és meddig nevű változót. Utána azokra lehet építeni a ciklust.

Megoldás

A releváns programrészlet:

/* Beolvasás */
printf("Mettol? ");
scanf("%d", &hatar1);
printf("Meddig? ");
scanf("%d", &hatar2);

/* Melyik nagyobb? */
if (hatar1 < hatar2) {
    mettol = hatar1;
    meddig = hatar2;
} else {
    meddig = hatar1;
    mettol = hatar2;
}

6. Adott hosszúságú vonal

Írj egy programot, amely kér a felhasználótól egy számot, és kirajzol egy + és jelekből álló szakaszt. Pl. ha a szám 4, akkor a képernyőn a lenti ábra jelenjen meg, vagyis a belsejében 4 db legyen:

Mekkora legyen a szakasz?
4
+----+

Írd meg a program pszeudokódját, utána pedig gépen a C forráskódot! A pszeudokódot írhatod a fejlesztőkörnyezetbe megjegyzésként is, az egyes sorok alá odaírva a C nyelvű megfelelőjüket.

Tipp: ehhez a programhoz nem kell if() elágazás. Ha olyan változatot írtál, amiben van, akkor próbáld meg anélkül is! Gondolj arra, hogy a szakasz elején és végén biztosan van +.

Megoldás

#include <stdio.h>

int main(void){
    int hossz;
    printf("Mekkora legyen a szakasz?\n");
    scanf("%d", &hossz);  /* Hossz beolvasása */

    printf("+");  /* A vonal elejére kell egy + jel */
    int i;
    for (i = 0; i < hossz; i = i+1)  /* Ciklus, ami kiír adott - jelet */
        printf("-");
    printf("+");  /* A vonal végére is kell egy + jel */

    return 0;
}

7. Adott méretű négyzet

|....|
+----+
|....|
|....|
|....|
|....|
+----+

Az előző feladat rajzolást végző programrészéből készíts egy olyan másolatot, amely + és - karakterek helyett | és . karaktereket használ, mint jobb oldalon! Végül pedig, ezt ciklusba téve, írj egy olyan változatot, amelyben egy adott méretű négyzet jelenik meg.

A forráskód szerkesztését kényelmesebb billentyűzeten végezni, mint egérrel. Szinte minden funkció elérhető gyorsbillentyűkön keresztül. Néhány fontosabb:

  • Home, End: sor elejére, sor végére ugrás,
  • Shift + , , , : forráskódrészlet kijelölése,
  • Tab, Shift + Tab: a kijelölt részlet indentálása (különösen hasznos ennél a feladatnál: amikor egy meglévő kódrészletet szeretnél ciklusba betenni, vagy ciklusból kivenni!),
  • Ctrl + C, Ctrl + V: a szokásos másolás és beillesztés,
  • Ctrl + Z: „undo”, szerkesztés visszavonása.

Megoldás

#include <stdio.h>

int main(void) {
    int hossz;
    printf("Mekkora legyen a negyzet? ");
    scanf("%d", &hossz);

    int i, j;

    /* felső sor */
    printf("+");
    for (i = 0; i < hossz; i = i+1)
        printf("-");
    printf("+\n");

    /* közepe, n-szer */
    for (j = 0; j < hossz; j = j+1) {
        printf("|");
        for (i = 0; i < hossz; i = i+1)
            printf(".");
        printf("|\n");
    }

    /* alsó sor, mint a teteje */
    printf("+");
    for (i = 0; i < hossz; i = i+1)
        printf("-");
    printf("+\n");

    return 0;
}

8. További feladatok

Ha elkészültél, folytasd a feladatgyűjtemény ehhez a témakörhöz kapcsolódó ciklusos feladataival!