C is een mid-level taal en het heeft een compiler nodig om het om te zetten in een uitvoerbare code, zodat het programma op onze machine kan worden uitgevoerd.
Hoe compileren en draaien we een C programma?
Hieronder staan de stappen die we gebruiken op een Ubuntu machine met gcc compiler.
- We maken eerst een C programma met behulp van een editor en slaan het bestand op als bestandsnaam.c
$ vi filename.c
- Het diagram rechts laat een eenvoudig programma zien om twee getallen op te tellen.
- Dan compileer je het met onderstaand commando.
$ gcc –Wall filename.c –o filename
- De optie -Wall schakelt alle waarschuwingsberichten van de compiler in. Deze optie wordt aanbevolen om betere code te genereren.
De optie -o wordt gebruikt om de naam van het uitvoerbestand op te geven. Als we deze optie niet gebruiken, dan wordt een uitvoerbestand met de naam a.out gegenereerd.
- Na het compileren is het uitvoerbare bestand gegenereerd en we voeren het gegenereerde uitvoerbare bestand uit met het onderstaande commando.
$ ./filename
Wat gebeurt er in het compilatieproces?
Compiler zet een C programma om in een uitvoerbaar bestand. Er zijn vier fasen voor een C programma om een executable te worden:
- Voorbewerking
- Compilatie
- Assemblage
- Linking
Door onderstaand commando uit te voeren, krijgen we alle tussenliggende bestanden in de huidige directory, samen met het uitvoerbare bestand.
$gcc –Wall –save-temps filename.c –o filename
De volgende schermafbeelding toont alle gegenereerde tussenliggende bestanden.
Laten we een voor een bekijken wat deze tussenliggende bestanden bevatten.
Voorbewerking
Dit is de eerste fase waar de broncode doorheen wordt gehaald. Deze fase omvat:
- Removal of Comments
- Expansion of Macros
- Expansion of the included files.
- Conditional compilation
De voorbewerkte output wordt opgeslagen in de bestandsnaam.i. Laten we eens kijken wat er in filename.i staat: using $vi filename.i
In de bovenstaande uitvoer is het bronbestand gevuld met heel veel informatie, maar aan het eind is onze code bewaard gebleven.
Analyse:
- printf bevat nu a + b in plaats van add(a, b) dat komt omdat macro’s zijn uitgebreid.
- Commentaren zijn gestript.
- #include<stdio.h> ontbreekt in plaats daarvan zien we heel veel code. Dus de header bestanden zijn uitgebreid en opgenomen in ons bronbestand.
Compileren
De volgende stap is het compileren van bestandsnaam.i en het produceren van een; tussenliggend gecompileerd uitvoerbestand bestandsnaam.s. Dit bestand is in assemblage niveau instructies. Laten we dit bestand eens doornemen met $vi filename.s
De momentopname laat zien dat het in assembleertaal is, die assembler kan begrijpen.
Assemblage
In deze fase wordt filename.s als invoer genomen en door assembler omgezet in filename.o. Dit bestand bevat instructies op machineniveau. In deze fase wordt alleen bestaande code omgezet in machinetaal, de functie-aanroepen zoals printf() worden niet opgelost. Laten we dit bestand eens bekijken met $vi filename.o
Linking
Dit is de laatste fase waarin alle functie-aanroepen met hun definities worden gelinkt. Linker weet waar al deze functies zijn geïmplementeerd. Linker doet ook wat extra werk, het voegt wat extra code aan ons programma toe die nodig is wanneer het programma start en eindigt. Er is bijvoorbeeld een code die nodig is voor het opzetten van de omgeving, zoals het doorgeven van commandoregel argumenten. Deze taak kan gemakkelijk worden gecontroleerd door gebruik te maken van $size filename.o en $size filename. Door deze commando’s weten we dat hoe het uitvoerbestand toeneemt van een object bestand naar een uitvoerbaar bestand. Dit komt door de extra code die de linker toevoegt aan ons programma.