Vaihe 1: Esikääntäjän käsittely
Vaihe 2: Käännös kooditiedostosta objektitiedostoksi
Vaihe 3: Linkitys objektitiedostosta suoritettavaksi ohjelmaksi
Ohjelma voi muodostua useista tiedostoista, joille suoritetaan ensin erikseen vaiheet 1 ja 2. Lopuksi tiedostot linkitetään kokonaiseksi ohjelmaksi vaiheessa 3.
#
-alkuisia komentoja, joita kutsutaan direktiiveiksi.
Tavallisia direktiivejä ovat #include
,
joka lisää koodin osaksi toisen tiedoston,
sekä #define
, joka määrittelee makron.
Olemme käyttäneet #include
-direktiiviä aiemmin
standardikirjaston yhteydessä, jolloin tiedoston nimi annetaan
kulmasuluissa (kuten <iostream>
).
Kun liitämme koodin osaksi oman tiedostomme, annamme puolestaan
tiedoston nimen lainausmerkeissä.
Seuraavassa esimerkissä tiedostossa koodi.cpp
on ohjelman koodi, jonka osaksi liitetään tiedostossa
lisa.inc
oleva koodi.
// lisa.inc cout << "Moikka!\n";
// koodi.cpp #include <iostream> using namespace std; int main() { #include "lisa.inc" }Tämä tarkoittaa samaa kuin seuraava koodi:
// koodi.cpp #include <iostream> using namespace std; int main() { // lisa.inc cout << "Moikka!\n"; }Direktiivi
#define
määrittelee makron,
joka tarkoittaa, että tietty merkkijono koodissa korvataan
toisella merkkijonolla. Esimerkiksi seuraava makro
korvaa merkkijonon pb
merkkijonolla push_back
.
#define pb push_backTämän makron seurauksena koodi
v.pb(1);muuttuu esikääntäjän käsittelyssä näin:
v.push_back(1);
koodi.cpp
ja testi.cpp
.
Tiedostossa testi.cpp
on funktio testi
,
jonka esittely on otsikkotiedostossa testi.h
.
Ideana on, että koodi.cpp
tietää otsikkotiedoston
ansiosta funktiosta testi
, vaikka se ei näe funktion määrittelyä.
// testi.h void testi();
// testi.cpp #include <iostream> using namespace std; void testi() { cout << "Moikka!\n"; }
// koodi.cpp #include <iostream> #include "testi.h" using namespace std; int main() { testi(); }Ohjelman käännöksessä annetaan molemmat tiedostot
koodi.cpp
ja testi.cpp
:
$ g++ koodi.cpp testi.cpp -o koodiTiedostot voi kääntää myös erikseen näin:
$ g++ koodi.cpp -c $ g++ testi.cpp -c $ g++ koodi.o testi.o -o koodiTässä kaksi ensimmäistä riviä luovat käännetyt objektitiedostot
koodi.o
ja testi.o
ja viimeinen rivi linkittää nämä tiedostot yhteen
suoritettavaksi ohjelmaksi.
main
-funktion
määrittelyä, jossa on parametrit argc
(parametrien määrä)
ja argv
(parametrit merkkitaulukkoina).
Esimerkiksi seuraava koodi tulostaa kaikki ohjelmalle annetut parametrit:
int main(int argc, char **argv) { for (int i = 0; i < argc; i++) { cout << i << " " << argv[i] << "\n"; } }Nyt voimme käynnistää ohjelman vaikkapa seuraavasti:
$ ./koodi apina banaani cembalo 0 ./koodi 1 apina 2 banaani 3 cembaloEnsimmäinen parametri on aina ohjelman nimi, eli
argc
on aina vähintään 1,
ja tämän jälkeen tulevat mahdolliset muut parametrit,
jotka on erotettu välilyönneillä.
main
-funktion tyyppi on int
,
se voi palauttaa jonkin kokonaisluvun.
Jos main
-funktiossa ei ole return
-riviä,
se palauttaa oletuksena luvun 0,
jonka tavallinen tulkinta on, että ohjelma päättyi onnistuneesti.
Jos ohjelma kuitenkin palauttaa jotain muuta,
tämä tulkitaan virhetilanteeksi.
Esimerkiksi seuraava ohjelma palauttaa virheen merkkinä luvun 13:
int main() { return 13; }Ohjelman suorituksen jälkeen voimme tarkastaa sen palautusarvon. Esimerkiksi Linuxissa pääsemme käsiksi palautusarvoon komentorivillä muuttujan
$?
kautta:
$ ./koodi $ echo $? 13Voisimme käyttää palautusarvoa esimerkiksi skriptissä, joka suorittaa ohjelman ja tarkastaa suorituksen jälkeen, päättyikö ohjelma onnistuneesti vai tapahtuiko jokin virhe.