I det här avsnittet kommer kompilatorn och dess uppgifter introduceras.
Kompilators uppgift är i stora drag att översätta programkoden skriven av programmeraren till maskinkod som datorn kan förstå och exekvera (köra). Det finns ett flertal olika kompilatorer som kan kompilera C-kod, en av de populäraste som vi kommer introducera lite kort och referera till är GNUs C-kompilator: gcc.
GCC är en kompilatorsamling som bl.a. klarar att kompilera C-kod, den är gratis och går att installera på Linux och OSX. För Windows kan man använda t.ex. Cygwin för att få tillgång till gcc.
Du skriver din programkod i en textfil med ändelsen .c, t.ex. prog.c. För att kompilera den till körbar kod kan du använda gcc via en terminal.
$ gcc prog.c
Har du skrivit korrekt kod så kommer du att få en fil som heter a.out vilken du kan köra genom:
$ ./a.out
För fullständig dokumentation av gcc se online-dokumentationen.
En kompilator består egentligen av flera olika program som sköter olika delar av kompileringsprocessen som alltså är en flerstegsprocess. Exakt hur den går till varierar mellan olika kompilatorer. Här ges en övergripande något förenklad bild som dock täcker in kompileringens huvuddelar. Nedan följer, i ordning, kompileringsprocessen:
Den så kallade preprocessorn går igenom koden för att behandla särskilda instruktioner som inte är del av själva C-koden. I C skrivs dessa preprocessorinstruktioner med ett inledande #-tecken. Ett exempel är t.ex. instruktionen “#define X Y” som byter ut X mot Y i koden, den här instruktionen kan t.ex. vara användbar för att hårdkoda konstanter.
#define HEIGHT 10
...HEIGHT...
efter preprocesseringen kommer koden istället lyda:
#define HEIGHT 10
...10...
För att det överhuvudtaget skall gå att översätta programkoden till maskinkod så krävs det att den är skriven efter de regler som sätts upp inom programmeringsspråket, i vårt fall C. Det är dessa regler som kallas för språkets syntax och reglernas innebörd kallas för semantik. Exempelvis kommer kompileringen inte gå igenom om programmeraren stavar fel och försöker anropa en subrutin som ej finns (syntaktiskt fel).
Kompilatorn försöker nu formulera om din kod så att den ska exekvera så snabbt som möjligt. Exempelvis så plockas ev. hantering av variabler som ej används bort. Det finns olika grader av optimering som i gcc anges med flaggorna -O1, -O2, -O3 för olika optimeringsgrader (det finns även andra typer av mer avancerade optimeringsflaggor). Notera att högre optimering ger längre kompileringstid, det kan därför vara en god idé att inte ha full optimering under utvecklings- och debugfasen. Standardvärde för optimering i gcc är -O2 vilket räcker fullgott i de flesta fall.
#-O3 anger att full optimering skall användas
$ gcc -O3 prog.c
Nu är det dags att skapa maskinkoden som kan exekveras av datorn. Kompilatorn genererar nu maskinkod som passar till din dators arkitektur, alltså hur den är uppbyggd. I vissa fall kan det vara nödvändigt att studera hur kompilatorn har lyckats med sin uppgift att översätta programkoden till maskinkod. Dock är maskinkoden inte speciellt lättläst, det är därför möjligt att begära av kompilatorn att den ska ge maskinkoden i form av Assemblerkod. Assemblerkoden ligger mycket nära maskinkoden men är mer lättläst då bl.a. instruktionskoder i maskinkoden bytts ut mot passande namn.
#-S anger att assemblerkoden ska skrivas till filen prog.s
$ gcc -S prog.s prog.c