Ú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!
- 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.
- 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!
- Most fordítsa le, és magyarázza meg a kapott hibajelzéseket!
- 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!
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.
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 előadásán (8. ea.) bemutatott dinamikus tömb példájához. Itt azonban nullával lezárt karaktersorozatot (sztringet) tárolunk dinamikus területen. 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.
- 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!
-
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: - 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.
- 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ő.
- 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! - 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.
- 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!) - 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áltoztatnia. 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).
- Az elkészítendő függvények prototípusát a string2.cpp fájl végére másoltuk, így azokat csak meg kell valósítania.
- 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!