Ebben a részben arról lesz szó, hogy ha már van egy vagy több forrásfájlunk, akkor abból hogyan tudunk parancssort használva futtatható programot létrehozni.
Ez a C++ programozás kezdőknek tananyag legelső részének folytatása.
Következő rész: az első program
Ennek a tananyagrésznek a tartalma:
- fordítás parancssorban, Windows operációs rendszeren
- fordítás parancssorban, Linux operációs rendszeren
Egyéb tudnivalók:
- milyen fájl az a.out?
- fordítás két lépésben
- fordítás több forrásfájl esetén
- fordítás különböző C++ szabványok esetén
- gyakran használt fordítási kapcsolók
- fordítás köztes lépsei
A parancssor egyik nagy előnye, hogy a használata általában nem sokat változik az évek során. Windowsban viszont a parancssoros fordítás előfeltételeinek telepítése, beállítása jellemzően nem parancssorban történik. Előfordulhat, hogy ennek a tananyagnak az elkészítése után fél évvel teljesen máshogy fog kinézni mondjuk a cygwin vagy a mingw telepítője. Nem ígérem, hogy amint bármi változás van, ezt a leírást módosítani fogom, de remélem, hogy programok telepítése senkinek nem okoz gondot.
És persze az is előfordulhat, hogy valaki esetleg váratlan hibaüzenetet kapjon valamelyik telepítés során, például azért, mert a számítógépén némileg eltérőek a rendszerbeállítások. Ez esetben rá kell keresni a hibaüzenetre az interneten, és ha kell, több megoldást is össze kell vetni az ott találtak közül.
Ha még nincs forrásfájlunk, és egy rövid példaprogrammal ki szeretnénk próbálni a parancssoros fordítást, akkor először nyissunk meg egy egyszerű szövegszerkesztőt (pl. jegyzettömb vagy gedit) vagy code editort (pl. Visual Studio Code vagy Notepad++), hozzunk létre egy új szöveges fájlt, amit mentsünk el mondjuk first_example_program.cpp néven (de természetesen más nevet is adhatunk neki, a lényeg, hogy a formátuma, vagy más néven kiterjesztése .cpp legyen).
Ebbe a fájlba másoljuk be a következő kódot:
#include <iostream>
int main() {
std::cout << "Hello World!\n";
}
Természetesen mentsük is el a fájlt :)
Ezt követően tudjuk lefordítani, vagyis a futtatható programot létrehozni belőle. Ennek módja különböző operációs rendszereken némileg eltérő.
Bizonyos operációs rendszerek esetén elképzelhető, hogy alapból telepítve van C++ fordító, de jó eséllyel nekünk kell telepíteni. Több C++ fordító is létezik, talán a 3 legismertebb a g++, CLang, msvc. A tananyagban főként g++-t fogunk használni, ami a GCC része, illetve Windowson a MinGW része.
Esetleg érdemes tudni, hogy a Visual Studio msvc fordítóval dolgozik, de az msvc fordítót a Visual Studiotól függetlenül is telepíthetjük parancssoros fordításhoz, erről viszont ebben a tananyagban nem lesz szó, mint ahogy a CLang telepítéséről sem. Ha sokat foglalkozunk C++ programozással, elképzelhető, hogy később a g++ helyett más fordítóra is szükségünk lesz, de a tananyag legtöbb példaprogramja esetén tök mindegy, hogy milyen C++ fordítót használunk. Kezdőként szerintem nem érdemes egyszerre több C++ fordítót is feltelepíteni, de azért nem árt tudni, hogy nem csak a g++ létezik.
Fordítás parancssorban Windows operációs rendszereken:
A parancssorban történő fordításra Windowson több lehetőség is van. Néhány példa:
- telepíthetjük és használhatjuk a cygwin nevű programot
- Windows 10-től használhatunk Windows subsystem for Linuxot
- használhatjuk a Windows beépített parancssorát (cmd)
cygwin telepítése
Töltsük le a cygwin telepítőjét innen:
https://cygwin.com/install.html
Nyissuk meg a Windows beépített parancssorát.
Ezt például úgy tudjuk megtenni, hogy a billentyűzetünkön egyszerre megnyomjuk a windows gombot és az R billentyűt, majd a felugró ablakba beírjuk, hogy cmd, majd megnyomjuk a billentyűzetünkön az entert.
Lépjünk be a parancssorban abba a könyvtárba, ahova letöltöttük a cygwin telepítőjét. Ezt valami ilyesmi parancssal tehetjük meg:
cd c:\Users\felhasznalonev\Downloads\
Ha beléptünk a cygwin telepítőjét tartalmazó könyvtárba, ott futtassuk ezek közül a parancsok közül valamelyiket.
Válasszuk ezt, ha csak ki szeretnénk próbálni a C++ forrásfájlok fordítását:
setup-x86_64.exe -P gcc-g++
Válasszuk ezt, ha a g++ fordító mellé szeretnénk telepíteni parancssoros szövegszerkesztőt (nano), fájlkezelőt (mc), debuggert/hibakeresőt (gdb), verziókezelőt (git)
setup-x86_64.exe -P gcc-g++ -P gdb -P git -P mc -P nano
Persze ennél akár több csomagot, programot is kiválaszthatunk akár parancssori paraméterként megadva, mint a fenti példákban, vagy akár a cygwin telepítőjénében kattintgatva. Az előbbire láthatunk még példát egy másik blogomon, mely a LibreOffice fejlesztéssel foglalkozik.
Jó tanács: ne telepítsünk fel mindenféle csomagot, programot, amit vélhetőleg nem is fogunk használni, mert sok helyet foglalnak, és lassítja a számítógépet, ha sok kis fájl van a háttértárolón.
(Ha valamit elfelejtettünk telepíteni, később elindíthatjuk a cygwin telepítőjét, és ugyanígy telepíthetjük a hiányzó csomagokat, programokat, az újonnan elindított telepítő nem törli a már korábban telepített dolgokat.)
A cygwin telepítése innentől kezdve lényegében letudható a next, next, finish gombokra történő kattintgatással.
A cygwin telepítőjében is kiválaszthatjuk a telepíteni kívánt csomagokat, programokat.
A cygwinben a Linux, és unix-szerű operációs rendszereken megszokott parancsokat és parancssori programokat használhatjuk... némi eltéréssel persze.
Fordítás cygwinben
A példaprogram kódjának beillesztése nano szövegszerkesztőben:
A példaprogram fordítása és futtatása cygwinben:
A fordításhoz tehát a következő parancsot használhatjuk:
g++ forrasfajl.cpp -o futtathato_program_neve.exe
A parancssoros fordítás további lehetőségeiről ennek a résznek a végén, az egyéb tudnivalókban lehet olvasni.
A program futtatásánál az exe kiterjesztés elhagyható, viszont az aktuális könyvtárat ki kell írni:
./futtathato_program_neve
Annak ellenére, hogy a cygwin egy Linuxos parancsértelmezőt hivatott helyettesíteni, a fordítás eredménye, vagyis a futtatható program nem Linux bináris, mint a WSL esetében, hanem Windows bináris.
Ámdebár amit a cygwines g++-szal fordítunk, azt csak cygwinben fogjuk tudni futtatni, a cmd-ben például nem.
Ha olyan futtatható programot szeretnénk cygwinben fordítani, ami nem csak a cygwinben fut, akkor használjuk a cygwinen belül a MinGW g++ fordítóját.
Ehhez a mingw64-x86_64-gcc-core és mingw64-x86_64-gcc-g++ csomagokat telepítsük cygwinben:
Ha az alap g++ csomagot nem telepítjük vagy töröljük, a mingw64-x86_64-gcc-g++ csomagot pedig telepítjük, akkor a g++ forrasfajl.cpp -o futtathato_program_neve.exe parancssal egy cygwintől függetlenül futtatható binárist generálunk, de ha mindkét fordító egyidejűleg telepítve van (a g++ és a mingw64-x86_64-gcc-g++), akkor használjuk a következő parancsot a cygwintől független futtatható program generálásához:
mingw32-g++ forrasfajl.cpp -o futtathato_program_neve.exe
Ezután a futtatható programot elindíthatjuk cmd-ben is.
Windows Subsystem for Linux telepítése
Ez a módszer legalább 16215-ös build verziójú Windows 10, vagy újabb operációs rendszeren működik.
A build verziót többféleképp is meg lehet nézni Windowsban. Egyik lehetőség, ha a Windows saját parancssorába (cmd.exe) beírjuk, hogy ver.
A cmd-t például úgy indíthatjuk el, hogy megnyomjuk egyszerre a billentyűzeten a windows gombot és az R billentyűt, majd a felugró ablakba beírjuk, hogy cmd utána pedig megnyomjuk az entert.
Egy másik lehetőség, windows gomb + R, majd winver.
Indítsuk el a powershellt rendszergazda jogosultsággal.
A powershellben futtassuk a következő parancsot:
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
Majd válasszuk a számítógép újraindítását.
A Windows storeban keressünk rá valamelyik WSL által támogatott Linux disztribúcióra, például Debian vagy Ubuntu. Ebben a leírásban megtaláljuk, hogy miket használhatunk még.
A választott Linux disztribúciót telepítsük fel.
Majd indítsuk el.
Első indításkor adjuk meg a használni kívánt felhasználónevet és jelszót.
Itt szintén a Linux és unix-szerű operációs rendszerek parancssorában megszokott parancsokat használhatjuk, de kicsit kevesebb korlátozással, mint a cygwinben. A cygwinben például nem működtek a csomagkezelők.
Telepítsük fel a fordításhoz szükséges csomagokat, programokat. Debian és Ubuntu alapú disztribúciókon például a következő parancssal:
sudo apt-get install build-essential
Más disztribúciókon előfordulhat, hogy az apt-től eltérő csomagkezelőt tudunk használni, például: yum, dnf, pacman.
Fordítás Windows Subsystem for Linuxban
A parancs, amivel fordíthatunk itt is hasonló, csak a futtatható program nevének a végére nem kell .exe kiterjesztés:
g++ forrasfajl.cpp -o futtathato_program_neve
A létrehozott indítható program viszont csak itt a WSL-ben, vagy Linux bashben futtatható, a Windows parancssorában (cmd), vagy a Windows fájlkezelőjében nem.
Előfeltételek telepítése a Windows beépített parancssorának (cmd) fordításhoz történő használatához
Természetesen a cmd-ben is csak akkor tudunk fordítani, ha előzőleg már feltelepítettünk valamilyen fordítót. Ez lehet akár egy fejlesztői környezet, például mondjuk a CodeBlocks által telepített fordító, de akár külön is telepíthetünk egyet.
A példaprogramok kipróbálásához teljesen megfelel például a MinGW, amit innen tölthetünk le:
Mivel nem olyan könnyű megtalálni, hogy hol kell letölteni, ezért feltöltöttem erről is néhány képernyőképet. Idővel persze lehet, hogy máshogy fog kinézni a MinGW honlapja.
A MinGW telepítője nem egészen a megszokott "next, next, finish"-szerű telepítő.
Nem kell mindent telepíteni, csak azt, amit használni is fogunk. Például ha nem tervezünk Adában, Fortranban, vagy Objective-C-ben programozni, akkor ezeknek a fordítóját teljesen felesleges telepíteni.
Next vagy finish gomb helyett elég szokatlan helyen található a telepítés lebonyolítását okozó menüpont :)
Ezt követően állítsuk be, hogy a parancssorban bármelyik könyvtárból elérhessük a g++ fordítót. Ehhez a rendszerbeállításokban (system settings) a path környezeti változóhoz (environment variable) hozzá adni a MinGW könyvtárán belüli bin könyvtárat.
A rendszerbeállításokat például a windows gomb + pause break billentyűkombinációval érhetjük el, vagy windows gomb + r, majd sysdm.cpl
Biztos sokan megtalálják billentyűkombinációk nélkül is, a billentyűkombinációkat csak azért szeretem, mert nem nagyon változnak a különböző Windows verziókban.
Kattintsunk a beállítások módosítása (change settings) ikonra.
Válasszuk a speciális (advanced) fült, majd kattintsunk a környezeti változók (environment variables) gombra.
Válasszuk ki a path változót majd kattintsunk a szerkesztés (edit) gombra. Ha egyedül használjuk a számítógépet, akkor mindegy, hogy a felhasználónevünkhöz tartozót, vagy az összes felhasználóhoz tartozót választjuk.
Kattintsunk a new gombra, majd írjuk be a MinGW könyvtárán belüli bin könyvtár elérési útját, majd természetesen okézzunk le mindent :)
Ha jól emlékszem, régebbi Windowsokban nem így külön sorba kellett beírni, hanem a path eddigi tartalmához hozzá kellett írni egy elválasztókaraktert (jelen esetben a pontosvessző), majd a szóban forgó könyvtár elérési útját.
Fordítás a Windows beépített parancssorában (cmd-ben)
Ezt követően elindíthatjuk a Windows parancssorát, vagy ha eddig már meg volt nyitva, újra kell indítani a változtatások érvénybelépéséhez.
Windows gomb + r, cmd
Lépjünk be abba a könyvtárba, ahol a lefordítandó cpp fájlunk található.
Itt futtassuk a következő parancsot:
g++ forrasfajl.cpp -o futtathato_program_neve.exe
A Windows parancssorában nem kell kiírni az aktuális könyvtár jelét az indítható fálj neve elé, vagyis nem kell ./ mint Linux/unix rendszereken, illetve az .exe kiterjesztést sem kell kiírni.
Fordítás parancssorban Linux operációs rendszereken:
Nyissunk meg egy parancssoros felületet (Ubuntuban például ctr+alt+t vagy ctr+alt+f3, mely utóbbi esetén (az Ubuntu verziójától függően) alt + f7 vagy alt + f1 billentyűkombinációval tudunk visszalépni grafikus felületre).
Linuxokon előfordulhat, hogy a c++ fordító (a g++, mely a GCC része) alapból telepítve van. Ezt a gcc -v vagy g++ -v parancsokkal tudjuk kideríteni.
Debian és Ubuntu alapú Linux disztribúciókon használjuk a következő parancsot a C és C++ fordításhoz szükséges alapvető csomagok telepítéséhez.
sudo apt-get install build-essential
A build-essential csomag tartalma.
Természetesen a build-essential csomag helyett hasonlóképpen lehet telepíteni csak a g++-t.
Más Linux disztribúciókon elképzelhető, hogy az apt helyett más csomagkezelőket kell használni a telepítéshez, például yum, dnf, pacman.
Ha a g++ fordító telepítve van, akkor valami ilyesmi kimenetet kéne látnunk a g++ -v parancs kimeneteként:
Ha a g++ fordító telepítve van, a fordításhoz lépjünk be abba a könyvtárba, ahol a lefordítandó cpp fájl található. Itt futtassuk a következő parancsot:
g++ forrasfajl.cpp -o futtathato_program_neve
A kimeneti fájlnak nem szükséges kiterjesztést adni (mint Windowsban), mivel a Linux alapú operációs rendszerek nem igénylik, hogy a fájlneveknek kiterjesztése legyen. Azért van mégis egyes fájloknak kiterjesztése Linuxon is, mivel vannak olyan alkalmazások, amik viszont igénylik a kiterjesztések használatát.
A lefordított programot parancssorban így futtathatjuk:
./fájlnév
Esetleg előfordulhat, hogy a lefordított fájlon nem lesz futtatási jog, és így hibaüzenetet kapunk az indítás megkísérlésekor. Erről úgy tudunk meggyőződni, hogy az ls -al parancs által megjelenített listában az adott fájlhoz tartozó első oszlop nem tartalmaz x karaktereket. Ekkor használjuk a chmod u+x ./fájlnév parancsot.
Egyéb tudnivalók
Milyen fájl az a.out?
Unix/Linux-szerű operációs rendszereken ha parancssoros fordításnál nem adjuk meg a futtatható program nevét, akkor az alapértelmezett (default) név a.out (assembly output) lesz. Ez egy futtatható fájl.
Tehát valami ilyesmi parancsnak az eredménye:
g++ forrasfajl.cpp
Windowsban a.out helyett a.exe jön létre.
Fordítás és linkelés
Előfordulhat, hogy bizonyos helyeken a futtatható program létrehozását két képésben végzik el, először elkészítik az object fájlt/fájlokat, majd az object fájlokból egy külön lépéssel hozzák létre a futtatható programot (ez utóbbit hívják linkelésnek, mivel a linker végzi el).
Parancssorban ez két külön parancsot jelent:
1. parancs
Unix/Linux-szerű operációs rendszereken:
g++ -c forrasfalj.cpp -o object_fajl_neve.o
1. parancs
Windowson:
g++ -c forrasfalj.cpp -o object_fajl_neve.obj
2. parancs
g++ object_fajl_neve.o -o futtathato_program_neve
Ügyejünk arra, hogy Linuxon .o, Windowson pedig .obj az object fájlok kiterjesztése, illetve Windowson .exe a futtatható fájlok kiterjesztése, Unix/Linux-szerű rendszereken viszont nem szükséges, hogy a futtatható fájloknak kiterjesztése legyen.
Másrészt arra is ügyeljünk, hogy ha object fájlt szeretnénk létrehozni, akkor a -c kapcsolót meg kell adnunk. Hiába írunk az -o kapcsoló után egy .o vagy .obj kiterjesztésű fájlt, ha nincs -c kapcsoló, akkor a sikeres fordítás végeredménye egy futtatható fájl lesz.
Az object fájlok nem futtatható fájlok. Például arra jók, hogy egy több forrásfájlból álló program esetén ha csak az egyik forrásfájlt módosítjuk, akkor csak abból a forrásfájlból kell újra object fájl létrehozni, a többiből nem. Vagyis ha csak egyetlen forrásfájlt módosítunk, nem kell újrafordítani azokat a fájlokat is, amiket nem is módosítottunk.
Több forrásfájlból álló programok
A tananyag elején egyelőre nem készítünk több forrásfájlból álló programokat, az ezzel kapcsolatos tudnivalókra a megfelelő tananyagrészben kitérünk.
Elöljáróban annyit el lehet mondani a témáról, hogy hasonló paranccsal fordíthatunk több fájlból álló programokat is, csak a különböző cpp fájlokat szóközzel egymástól elválasztva soroljuk fel. Az úgynevezett header fájlokat (.h, .hpp vagy .hxx kiterjesztésű fájlokat) ne soroljuk fel a bemeneti fájlok között.
A C++ nyelv különböző szabványai
C++11-es szabvány szerint érvényes kódrészletet tartalmazó forráskód esetén a fordításhoz használt parancsban a sikeres fordításhoz szerepelnie kell az std=c++11 kapcsolónak:
g++ -std=c++11 forrasfajl.cpp -o futtathato_program_neve
Hasonlóképp kell eljárnunk C++14, C++17 és C++20-as szabvány szerint érvényes forrásfájlokkal.
Gyakran használt g++ kapcsolók (angolul option)
-Wall: ne csak a fordítási hibákat (angolul error) írja ki a fordító, hanem a lényegesebb figyelmeztetéseket (angolul warning) is.
-Wextra: a fordító egyéb warningokat is írjon ki.
Fontos: a Wextra kapcsoló használata nem foglalja magában a Wall kapcsoló warningjait, ezért érdemes őket együtt használni.
Itt található egy lista, hogy a Wall és Wextra kapcsolók segítségével a fordító milyen warningokat mutat meg a kódban.
A Wall és Wextra kapcsolók együttes használata sem mutatja meg az összes lehetséges warningot. Vannak egyéb warningok is, amiket esetleg érdemes lehet debuggoláshoz használni a későbbiek során.
-g: a debug (nyomkövető) mód használatához szükséges információk elhelyezése az object fájlokban és az indítható állományban.
Néhány megjegyzés a -g kapcsolóval kapcsolatban:
- Kerülendő a debug információkkal lefordított programot a felhasználóknak odaadni. Egyrészt mert lassabb, másrészt mert a program működése könnyebben visszafejthető.
Persze ha mondjuk egy egyetemi beadandóról van szó, akkor jó eséllyel lényegtelen, hogy debug információkkal vagy anélkül adjuk oda a futtatható programot a tanárnak. - Ha már korábban fordítottuk a programunkat enélkül a kapcsoló nélkül, akkor előfordulhat, hogy törölnünk kell a korábban lefordított fájlokat ahhoz, hogy ez a beállítás érvénybe léphessen. És ez visszafele is igaz, vagyis ha már -g kapcsolóval fordítottuk a programot, de azt szeretnénk, hogy mégse kerüljenek bele a debug információk, akkor törölni kell a fordított fájlokat mielőtt -g kapcsoló nélkül elvégeznénk a fordítást.
- Ha használjuk a -g kapcsolót, akkor ne használjunk optimalizációt (pl. -O3 kapcsoló), mert a debuggolás nem fog rendeltetésszerűen működni.
-pedantic: a fordító hibaüzenetben kiírja a kód nem szabványos részeit. Nem szabványos kódrészlet az, amit nem minden fordító tud futtathatóvá alakítani. Egy példa (VLA tömb):
int elemszam;
cin >> elemszam;
int tomb[elemszam];
A kód hordozhatósága miatt (ha esetleg más rendszeren vagy más fordítóval szeretnénk később dolgozni) javasolt a forráskódnak lehetőség szerint csak szabványos kódrészletet tartalmaznia.
-O3 (nem nulla, hanem nagy o betű): a fordító a sebességre optimalizálja a kódot.
-Os: a fordító az alacsony helyfoglalásra optimalizálja a kódot.
Ugyanazon optimalizációk más platformokon különböző eredményt fognak produkálni.
-fexceptions: kivételkezelés ( azaz try { } catch ( ) { } blokkok ) engedélyezése a forráskódban. Alapértelmezetten be van kapcsolva, viszont vannak olyan projektek, amikben ki van kapcsolva, ami gyorsít(hat)ja a programot.
Ha esetleg C++ kódban tiltani szeretnénk a kivételkezelést, akkor az -fno-exceptions kapcsolót használhatjuk.
A fordítás köztes lépéseinek eredményei:
A preprocesszálás, más néven előfeldolgozás vagy előfordítás eredményét kiíratjuk a parancssorba:
g++ -E forrasfajl.cpp
Ez általában több ezer sor, így érdemesebb inkább fájlba menteni:
g++ -E forrasfajl.cpp > fajlnev.txt
(Ha már létezik ilyen nevű fájl > jel esetén felülírja, >> jel esetén pedig az eddigi tartalmához hozzáfűzi).
Az assembly kóddá történő fordítás eredményét menjük el a fajlnev.s nevű fájlba:
g++ -S fajlnev.cpp
Teljesen más assembly kódot eredményez, ha optimalizációt is beállítunk, például:
g++ -O3 -S fajlnev.cpp
Az object fájllá történő fordítás eredményét kapjuk meg a -c kapcsolót használva, melyre kicsit fentebb láthatunk példát.
Egyéb
További kapcsolókról például itt lehet olvasni.
Következő rész: az első program