Útmutató a 2. laborhoz

Laborfeladatok: megoldásokkal

A laborfoglalkozás végén ne felejtse el feltölteni megoldásait a Jporta rendszerbe, hogy megkapja a labor elvégzéséért járó pontot! A feladatok feltöltésére a laborgyakorlatot követő szombat 06:00-ig van lehetősége.

Az első 4 feladat megoldására tervezett idő max. 15 perc!


    A Git tárolók webes felületén keresztül zip, tar, stb. formátumban is letölthető egy projekt. A Gitlab rendszer webes felületén a projekt nyitó lapján található download gombbal választhatjuk ezt ki.
  1. Kérje le a 2. laborhoz előkészített projektet a tantárgy Git tárolójából https://git.ik.bme.hu/Prog2/labor_peldak/lab_02! A második labor anyaga több katalógusba van szervezve. Fájlkezelővel ellenőrizze, hogy mi töltődött le! Sikeres letöltés esetén 3 db fáljt (README.md, const_h.cpp, ref_h-cpp) és 2 db katalógust (string1, string2_cpp) kell látnia.
  2. Indítson egy C++ fejlesztőkörnyezetet (CodeBloks, Visual Studio, ...) és nyissa meg vele a const_h.cpp fájlt (húzza bele az egérrel)! A program lefordítása nélkül keresse meg, hogy mely sorok hibásak és miért!
  3. Most fordítsa le, és magyarázza meg a kapott hibajelzéseket!
  4. Ugyanez a feladat a ref_h.cpp-vel is! Először gép nélkül, majd a fordítóprogram segítségével keresse meg a hibákat! A laborvezetővel beszélje meg, hogy miért nem lehet lokális változó címét, vagy referenciáját függvényértékként visszaadni!

Dinamikus sztring

A továbbiakban sztring dinamikus tárolásához alkalmas adatstruktúrát és azon műveleteket végző függvényeket készítünk C, majd C++ nyelven.

A feladat megoldása nagyon hasonló az előző félév (C) egyik gyakorlatán (9. gyak) bemutatott halmaz adatstruktúra példájához: A nullával lezárt karaktersorozatot (sztringet) dinamikus területen tároljuk. A terület kezdőcímét és a sztring hosszát pedig egy struktúrában. A kialakított adatstruktúra C nyelven a következő:

typedef struct String {
    char *pData;        /// pointer nullával lezárt karaktersorozatra
    unsigned int len;   /// hossz lezáró nulla nélkül
} String;

A gyorsabb haladás érdekében a C nyelvű programot előre elkészítettük, bár hagytuk benne két hibát. Ezt Önnek kell megtalálnia, majd a hibamentes kódot átalakítani úgy, hogy az kihasználja a C++ nyelv eddig megismert (nem OO) lehetőségeit.
A String adatstruktúrán műveleteket végző függvények nevébe belekódoltuk a művelet nevét, valamint azt, hogy String típusú adaton végzi a műveletet. Minden függvénynek első paramétere egy String típusú pointer, ami cél operandusa vagy forrás operandusa az adott műveletnek.

    Ha projektfájlra kattintva nem indul a CodeBloks, indítsa azt el, majd húzza rá a megnyíló ablak fejlécére a projektfájlt!
  1. A string1 katalógusban található string1.c és string1.h fájlok a feladat C nyelvű megvalósítását tartalmazzák. Nyissa meg a projektet! Ha nem tudja az előkészített projektet megnyitni, úgy hozzon létre egy üres C projektet (konzol alkalmazás), és adja hozzá a string1 katalógusban levő *.c és *.h fájlokat!
  2. Tanulmányozza a string1_main.c, string1.h és a string1.c fájlokat! A memtrace.* fájlokkal most ne foglalkozzon! Értse meg a programot! Figyelmesen olvassa el a megjegyzéseket is!
    A CodeBloks alapértelmezett színsémája szürkén jeleníti meg az egyszerű kommenteket. Ezt célszerű átállítani a Settings->Editor->Syntax highlighting panelen:
  3. A dinamikus memóriakezeléssel kapcsolatos hibák felfedezése alapfontosságú. Különösen fontosnak tarjuk, hogy egy kész programban ne legyen memóriafogyás. Hibák felfedezésére C/C++ programokban számos módszert használhatunk. Így pl:
    • Használhatjuk fejlesztő környezet nyújtotta eszközöket (google sanitizers, clang-tidy, VS beépített ellenőrzője, stb.)
    • Használhatunk külső eszközöket (Valgrind, duma, ...)
    • Használhatunk az előző félévben látott (debugmalloc) megoldásához hasonló preprocesszoros módszert. Ennek lényege, hogy a preprocesszor segítségével a forrásprogramban levő összes dinamikus memóriakezelő (malloc, free, new, delete) függvényhívást lecseréljük. Ehhez minden forrásfájlban a standard include állományok include-ja után include-olni kell a memtrace.h állományt és a programhoz hozzá kell fordítani/szerkeszteni a memtrace.c (C++ esetén memtrace.cpp) állományt.
  4. Az előkészített feladat a "memtrace" megoldást tartalmazza. Azért, hogy könnyen kikapcsolható legyen az ellenőrzés (a letesztelt, hibamentes programban már nincs rá szükség), a működést a MEMTRACE makró vezérli. Bekapcsoláskor ezt a makrót minden fájlban definiálni kell (még a memtrace.c-ben is), ezért célszerű ezt a fordító parancssorából, vagy a fejlesztő környezetből vezérelni.
    • Visual C++ esetében a Project->Properties->Preprocessor ablakban állíthatjuk be a megfelelő értéket:

    • CodeBloks-ban Project->Properties->Build targets->Build options->#defines ablakban definiálhatunk makrókat:


      A letöltött projektfájlokban a MEMTRACE makrót előre definiáltuk.

    • UNIX/Linux parancssoros környezetben a C fordító -D kapcsolójával definiálható a megfelelő makró. A letölthető Makefile-t úgy alakítottuk ki, hogy pl: a make CMD=-DMEMTRACE paranccsal memtrace változat állítható elő.
  5. Fordítsa le és futtassa a programot! Figyelje meg a createStringFromChar() függvényben elhelyezett durva hiba hatását!
    A memtrace a hibás memóriaterület környezetéről memória dump-ot készít, mely formájában hasonlít a hexdump -C program kimenetéhez.
    Ha nem jelentkezik ez a hiba, akkor nem kapcsolta be a MEMTRACE makróval az ellenőrzést!

  6. Törölje a createStringFromChar() függvényből a hibát okozó sort, és fordítsa le újból a programot, futtasson! Ellenőrizze, hogy a kapott eredmények (kiírások) jók-e! A helyes (elvárt) eredményt megjegyzésként megtalálja a forráskódban.
  7. Sajnos van még egy hiba: A futás során memóriaszivárgást kell észlelnie! A szivárgás tényét a program a standard kimenetén jelzi:


    A hibát az addChar2String függvény okozza! Javítsa ki a függvényt! (A hiba megtalálásában segít, hogy a függvényben csak egyetlen üres sor van. Onnan hiányzik valami!)

  8. Ha hibátlan a C változat, alakítsa át a programot úgy, hogy használja a C++ eddig tanult lehetőségeit (függvények túlterhelése, referencia, konstans, iostream, new, delete)! Az átalakítást a következő lépésekkel végezze el:
    • Lépjen át a string2_cpp katalógusba, ahol előkészítettünk egy C++ projektet. Használja az előkészített projektet, vagy hozzon létre egy új C++ projektet, adja hozzá a string2_cpp katalógusban levő *.c és *.h fájlokat!
    • Tanulmányozza a katalógusban található állományokat!
      • A string2.h a C verzió string1.h fájljához hasonló szerkezetben és sorrendben deklarálja a String adatstruktúrát és struktúrán műveletet végző függvényeket. Vegye észre, hogy a createStringFromChar és a createStringFromCstr függvényekből createString lett! Vegye észre továbbá, hogy a pointerek helyett referenciával adjuk át a String típusú paramétereket.
      • A memtrace* és a gtest_lite.h fájlok a teszteléshez kellenek. Ezekben most nem kell elmerülnie, változtatni sem kell bennük semmit.
      • A string2_main.cpp fájt sem kell változtatna. Ez az állomány a string1_main.c megfelelője, ami a funkciók kipróbálását segíti. Ebben a gtest_lite EXPECT_... makrók segítségével egyszerű teszteseteket alakítottunk ki. Az első teszteset pl. az üres sztring létrejöttét ellenőrzi:
        TEST(String1, Inic) {                // teszteset kezdete, neve és funkciója
          String a; createString(a);
          EXPECT_EQ(0u, StringLen(a));       /// 0 hosszúságúnak kell lennie
          EXPECT_EQ((char const*)(NULL), StringC_str(a));   /// null pointert várunk
        } END
        

        A TEST makró a teszteset kezdetét az END pedig a végét jelöli. A makrók között létrejövő blokkok (csukó/nyitó kapcsos zárójelek) a névütközések elkerülését segítik. Így ugyanazokat az azonosítókat több tesztesetben is lehet használni. Az elvárt működés ellenőrzését összehasonlító makrók támogatják (EXPECT_EQ, EXPECT_TRUE, EXPECT_DOUBLE_EQ, stb.). Ezek működését és használatát nem kell most megértenie.

      • A string2.cpp fájl a string1.c megfelelője, azaz a műveleteket végző függvények megvalósítási helye. Ebben az állományban elkezdtük megvalósítani az egyes függvényeket C++ nyelven.
      • Valósítsa meg a string2.cpp fájlban a hiányzó függvényeket C++ nyelven! Összesen 5 függvényt kell megvalósítania, amit sokban segít az előző feladatrészben elkészített C nyelvű változat (string1.c).
        • A függvények prototípusát a fájl végére másoltuk string2.h fájlból, így abban csak az ELKESZULT makrót kell módosítania (ld. alább).
        • A string2.h fájlban definiált ELKESZULT makró értékét annak megfelelően állítsa 0,1,2,3,4 vagy 5 értékűre, hogy hol tart a feladat megoldásában! Ha folyamatosan halad, akkor a kód mindig fordítható és tesztelhető lesz.

      • A töltse fel az elkészített string2.cpp és string2.h fájlokat a Jporta rendszerbe (String2, önellenőrző feladat)!
    • Szorgalmi feladat:
      • Készítsen insertert ( ostream& operator<<(ostream&, const String&) ) sztringek kiírásához.

    Jó munkát!

    Utolsó frissítés: 2021-02-15 13.11