Tie koodariksi

C++-ohjelmointi

Luku 16: Käännös ja suoritus

Käännösprosessi

C++-ohjelman käännös muodostuu kolmesta vaiheesta:

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.

Esikääntäjä

Ennen koodin varsinaista kääntämistä koodin käsittelee esikääntäjä, jolle voi antaa #-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_back
Tämän makron seurauksena koodi
v.pb(1);
muuttuu esikääntäjän käsittelyssä näin:
v.push_back(1);

Monta tiedostoa

Seuraava ohjelma muodostuu kahdesta käännettävästä tiedostosta 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 koodi
Tiedostot voi kääntää myös erikseen näin:
$ g++ koodi.cpp -c
$ g++ testi.cpp -c
$ g++ koodi.o testi.o -o koodi
Tässä kaksi ensimmäistä riviä luovat käännetyt objektitiedostot koodi.o ja testi.o ja viimeinen rivi linkittää nämä tiedostot yhteen suoritettavaksi ohjelmaksi.

Komentoriviparametrit

Kun ohjelma käynnistetään komentoriviltä, sille on mahdollista antaa parametreja nimen jälkeen. Nämä parametrit saa selville ohjelmassa käyttämällä laajempaa 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 cembalo
Ensimmä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ä.

Palautusarvo

Koska 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 $?
13
Voisimme käyttää palautusarvoa esimerkiksi skriptissä, joka suorittaa ohjelman ja tarkastaa suorituksen jälkeen, päättyikö ohjelma onnistuneesti vai tapahtuiko jokin virhe.