Útmutató a 0. laborhoz

Laborfeladatok:

A laborfoglalkozás végén töltse fel az elkészített f1.txt jegyzőkönyvet a Jporta rendszerbe (0. laborfeladat), hogy megkapja a labor elvégzéséért járó pontot! A laborgyakorlatokon elvégzett feladatok feltöltésére a minden esetben laborgyakorlatot követő szombat 06:00-ig van lehetősége.

Eszközök

A laborfeladatok megoldásához tetszőleges fejlesztőkörnyezetet használhat. A HSzK laborjaiban a CodeBlocks, VSCode, ill. a VisualC++ érhető el, de számos más eszköz is létezik a C++ programok fejlesztéséhez, amelyeket saját gépén, vagy a kari felhőben kialakított gépen ki is próbálhat/használhat.

A fordítás lépései

A következő feladatokhoz készítsen egy szöveges (f1.txt), rövid jegyzőkönyvet. A jegyzőkönyvben írja le, hogy mit tapasztalt, milyen kimenet jelent meg a képernyőn, az megegyezett-e a várt kimenettel. A jegyzőkönyv első sorába írja bele a NEPTUN kódját!

  1. Indítsa el a MSYS2 UCRT64 környezetet, az MSYS2-ről bővebben itt olvashat. Keresse meg a home könyvtárát a számítógépgép meghajtóján! (A pwd parancs hasznos, keresse a C meghajtó/msys64 könyvtárban)
  2. Készítsen egy egyszerű c++ programot (main.cpp) a következő tartalommal. Tetszőleges szerkesztő használható, pl. nano, emacs vagy vim a konzolban, illetve az egyszerűen Notepad++-t használhatjuk a windows felől is. (a korábban megtalált helyre elnavigálva) Ugyanakkor a fordításhoz tartozó parancsokat már az MSYS2 UCRT64 konzolban adjuk ki! Kezdőknek elsősorban a nano szerkesztő javasolt! A vim szerkesztőhöz összefoglaló: VIM, alapok - kezdőknek .

    Nano szerkesztővel: nano main.cpp, a megjelent ablakban tudjuk a kódot szerkeszteni. Elmententéshez lépjünk ki a Ctrl+X-el és vállasszuk a mentést (Y).

    Például nvim esetén: nvim main.cpp majd i (szerkesztői mód), a kód, nyilakkal navigálás, szerkesztés, majd ESC és végül elmentjük és kilépünk: :wq A konzolba való beillesztéshez használja a jobb egérgomb, beillesztés funkciót!

    #include <iostream>
    int main() {
            std::cout << "Hello" << std::endl;
            return 0;
    }
    
  3. A futtatható állomány előállításához a háttérben a következő lépések hajtódnak végre: előfeldolgozás (preprocesszálás), fordítás majd linkelés. Ezt a g++ és clang++ esetén külön-külön lépésekben végre tudjuk hajtani. Nézzük meg a g++ --help parancs kimenetét! Milyen opciókat tudunk beállítani a kapcsolókkal? Írja bele a jegyzőkönyvbe azokat, amiket fontosnak talál!
  4. Adjuk ki a g++ -E main.cpp parancsot. Láthatjuk, hogy az include le lett cserélve az iostream-hez tartozó tartalomra. A preprocesszort semmilyen módon nem érdekli a C/C++ szintaktikája és szemantikája, egyszerű szövegfeldolgozást végez.
  5. Feladat: Van egy állományunk nagyon sok "ALMA", "SZILVA", "KORTE" szavakkal. Cseréljük le ezeket számokra a preprocesszort használva! Segítség: nevezzük át az állományt .cpp (pl. gy.cpp) kiterjesztésűre, és használjuk a #define makrót! Próbáljuk ki a következő fájlon:
    #define ALMA 0
    #define KORTE 1
    #define SZILVA 2
    ALMA,KORTE,KORTE,SZILVA,ALMA,ALMA ...
    
  6. A következő fázis a fordítás: Ekkor a forrásfájlokból az előfeldolgozást követően object állomány (.o/.obj) készül. Ezek a fájlok ugyan gépi utasításokat tartalmaznak, de közvetlenül nem futtathatóak. Hozzuk létre a következő állományokat, majd fordítsuk le őket a g++ -c main2.cpp és g++ -c foo.cpp parancsokkal (két .o kiterjesztésű fájlt kapunk, ls parancs).
    // main2.cpp:
    void foo(const char*);
    int main() {
            foo("Hello");
            return 0;
    }
    // foo.cpp:
    #include <iostream>
    void foo(const char* str) {
            std::cout << str << std::endl;
    }
    
  7. A két object fájl összeállításához adjuk ki a következő parancsot: g++ main2.o foo.o -o main2. Végül futassuk a programot: ./main2. Mivel azonos névkonvenciók szerepeltek, a linkelés megtalálta a foo függvény definícióját a másik állományban. Mivel a deklarációkat nehéz karbantartani, ha nagyon sok helyen használunk egy-egy függvényt, ezért szokás azt egy fejlécfájlba kiszervezni és utána azt include-olni. (Ahogy korábban is tanultuk)
  8. Egy függvényt akárhányszor deklarálhatunk, viszont csak egyszer definiálhatunk (One Definition Rule). Ez akkor okozhat problémát, ha egy header fájlban definíció is szerepel, ekkor az include-ok miatt előfordulhat, hogy több definíció is előáll egy-egy függvényből. Ezt elkerülendő, makrókat használunk a "felesleges" definíciók kiszűrésére: Ezt szokás include guard-nak hívni. Ezek az őrök eldobják a fejlécfájl tartalmát, ha már feldolgozásra került. Példa:
    // main3.cpp
    #include "bar.h"
    #include "bar.h" // jöhet egy másik include-ból is
    int main() {
            return 0;
    }
    // bar.h
    #ifndef BAR_H //include guard
    #define BAR_H
    
    void bar() { }
    
    #endif
    
  9. Nézzük meg mi lesz a preprocesszor kimenete: g++ -E main3.cpp Ez miért jó nekünk? Próbáljuk ki úgy is, ha kitöröljük az include guard-ot!
  10. Érdemes bekapcsolni a figyelmeztetéseket a fordításkor, ezt a -Wall kapcsolóval tudjuk megadni, ami több lehetséges hibaforrás azonosításában is segítségünkre lehet. Készítsünk egy új forrásfájlt warnings.cpp néven, a lenti tartalommal, mi az ami problémát okozhat? Ha megvan a válasz, fordítsuk le a következő paranccsal: g++ -Wall -o warnings warnings.cpp Ezt a figyelmeztető üzenetet vártuk?
    #include <iostream>
    int main() {
            int a;
            std::cout << a << std::endl;
            std::cout << "Hello" << std::endl;
            return 0;
    }
    
  11. Milyen funkciókat nyújtanak a -g, -Werror és -O0 kapcsolók? Nézzen utánna! Hasznos oldal ehhez: g++ file -g -Wall -Werror -O0
  12. Készítse el a lenti C++ kódot! A nyelv folyamatosan fejlődik, új standardokkal, verziókkal bővül. Fontos, hogy ezek a verziók visszafele kompatibilisek legyenek.
    //auto.cpp
    #include <iostream>
    int main() {
            auto i = 10 + 2;
            std::cout << i << std::endl;
            return 0;
    }    
    
  13. A verziókat az -std=[verzió] kapcsolóval állíthatjuk, ha a használt fordító támogatja a kiválasztott standardot. Próbáljuk ki a fenti kódon a következő standardokat:
    • g++ -std=c++98 -o auto auto.cpp
    • g++ -std=c++03 -o auto auto.cpp
    • g++ -std=c++11 -o auto auto.cpp
    • g++ -std=c++14 -o auto auto.cpp

    Mikor, melyik verzióban jelent meg valószínűleg ez az 'auto' kulcsszó?

Ha elkészült a feladattal, kérem mutassa be azt a laborvezetőnek, továbbá a megírt jegyzőkönyvet töltse fel a Jporta portálra a "0. laborfeladat" beadáshoz.

Ellenőrző feladat

Készítse el az első ellenőrző feladatot (Neptun), majd töltse fel azt a JPorta portálra, a feladat leírását szintén a portálon találja!
Végezzel el a fordítás lépéseit a feladaton! Mi a különbség a gcc és g++ fordítók között? Milyen paranccsal történt a fordítás a jporta portálon? (Fordítás/szabványos kimenet)

A félév további részében az ellenőrző feladatokat önállóan, otthon kell elkészíteni, általában a laborfoglalkozást megelőző kedd reggelig!

Utolsó frissítés: 2025-02-11 11.03