C Language
Compilatie
Zoeken…
Invoering
De C-taal is traditioneel een gecompileerde taal (in tegenstelling tot geïnterpreteerd). De C-standaard definieert vertaalfasen en het product van het toepassen ervan is een programmabeeld (of gecompileerd programma). In c11 worden de fasen vermeld in §5.1.1.2.
Opmerkingen
Bestandsnaamextensie | Beschrijving |
---|---|
.c | Bron bestand. Bevat meestal definities en code. |
.h | Koptekstbestand. Bevat meestal aangiften. |
.o | Objectbestand. Gecompileerde code in machinetaal. |
.obj | Alternatieve extensie voor objectbestanden. |
.a | Bibliotheekbestand. Pakket objectbestanden. |
.dll | Dynamic-Link-bibliotheek op Windows. |
.so | Gedeeld object (bibliotheek) op veel Unix-achtige systemen. |
.dylib | Dynamic-Link Library op OSX (Unix-variant). |
.exe , .com | Windows uitvoerbaar bestand. Gevormd door objectbestanden en bibliotheekbestanden te koppelen. In Unix-achtige systemen is er geen speciale bestandsnaamextensie voor uitvoerbaar bestand. |
POSIX c99 compiler vlaggen | Beschrijving |
---|---|
-o filename | Naam uitvoerbestand, bijv. ( bin/program.exe , program ) |
-I directory | zoeken naar headers in direrctory . |
-D name | definiëren macro name |
-L directory | zoek naar bibliotheken in de directory . |
-l name | link bibliotheek libname . |
Compilers op POSIX-platforms (Linux, mainframes, Mac) accepteren deze opties meestal, zelfs als ze geen c99
worden genoemd.
GCC (GNU Compiler Collection) vlaggen | Beschrijving |
---|---|
-Wall | Hiermee worden alle waarschuwingsberichten die algemeen worden geaccepteerd, nuttig geacht. |
-Wextra | Maakt meer waarschuwingsberichten mogelijk, kan te luidruchtig zijn. |
-pedantic | Waarschuwingen afdwingen waar code de gekozen norm schendt. |
-Wconversion | Schakel waarschuwingen in voor impliciete conversie, wees voorzichtig. |
-c | Compileert bronbestanden zonder te linken. |
-v | Druk compilatie-info af. |
-
gcc
accepteert de POSIX-vlaggen plus vele andere. - Veel andere compilers op POSIX-platforms (
clang
, leverancierspecifieke compilers) gebruiken ook de vlaggen die hierboven worden vermeld. - Zie ook GCC aanroepen voor veel meer opties.
TCC (Tiny C Compiler) vlaggen | Beschrijving |
---|---|
-Wimplicit-function-declaration | Waarschuw voor impliciete functieverklaring. |
-Wunsupported | Waarschuw voor niet-ondersteunde GCC-functies die door TCC worden genegeerd. |
-Wwrite-strings | Maak stringconstanten van het type const char * in plaats van char *. |
-Werror | Compilatie afbreken als waarschuwingen worden gegeven. |
-Wall | Activeer alle waarschuwingen, behalve -Werror , -Wunusupported en -Wwrite strings . |
De linker
De taak van de linker is het koppelen van een aantal objectbestanden ( .o
bestanden) in een binair uitvoerbaar bestand. Het koppelingsproces omvat voornamelijk het omzetten van symbolische adressen in numerieke adressen . Het resultaat van het koppelingsproces is normaal een uitvoerbaar programma.
Tijdens het koppelingsproces haalt de linker alle objectmodules op die op de opdrachtregel zijn opgegeven, voegt een systeemspecifieke opstartcode toe en probeert alle externe verwijzingen in de objectmodule op te lossen met externe definities in andere objectbestanden (objectbestanden) kan rechtstreeks op de opdrachtregel worden opgegeven of kan impliciet via bibliotheken worden toegevoegd). Het zal dan toewijzen belasting adressen voor het object bestanden, dat wil zeggen, het wordt aangegeven waar de code en data zal eindigen in de adresruimte van het voltooide programma. Als het eenmaal de laadadressen heeft, kan het alle symbolische adressen in de objectcode vervangen door "echte", numerieke adressen in de adresruimte van het doel. Het programma is nu klaar om te worden uitgevoerd.
Dit omvat zowel de objectbestanden die de compiler heeft gemaakt op basis van uw broncodebestanden als objectbestanden die vooraf voor u zijn gecompileerd en zijn verzameld in bibliotheekbestanden. Deze bestanden hebben namen die eindigen op .a
of .so
, en u hoeft ze normaal gesproken niet te weten, omdat de linker weet waar de meeste zich bevinden en ze indien nodig automatisch koppelen.
Impliciete aanroep van de linker
Net als de pre-processor is de linker een afzonderlijk programma, vaak ld
(maar Linux gebruikt bijvoorbeeld collect2
). Evenals de pre-processor wordt de linker automatisch voor u aangeroepen wanneer u de compiler gebruikt. De normale manier om de linker te gebruiken is dus als volgt:
% gcc foo.o bar.o baz.o -o myprog
Deze regel vertelt de compiler om drie objectbestanden ( foo.o
, bar.o
en baz.o
) te baz.o
in een binair uitvoerbaar bestand met de naam myprog
. Nu heb je een bestand genaamd myprog
dat je kunt uitvoeren en dat hopelijk iets myprog
en / of nuttigs zal doen.
Expliciete aanroep van de linker
Het is mogelijk om de linker direct op te roepen, maar dit is zelden aan te raden, en is meestal zeer platform-specifiek. Dat wil zeggen, opties die op Linux werken, werken niet noodzakelijkerwijs op Solaris, AIX, macOS, Windows en op dezelfde manier voor elk ander platform. Als u met GCC werkt, kunt u gcc -v
om te zien wat namens u wordt uitgevoerd.
Opties voor de linker
De linker neemt ook enkele argumenten om zijn gedrag te wijzigen. De volgende opdracht zou gcc vertellen om foo.o
en bar.o
te koppelen, maar ook de bibliotheek ncurses
.
% gcc foo.o bar.o -o foo -lncurses
Dit is eigenlijk (min of meer) equivalent aan
% gcc foo.o bar.o /usr/lib/libncurses.so -o foo
(hoewel libncurses.so
zou kunnen zijn libncurses.a
, dat is gewoon een archief gemaakt met ar
). Merk op dat u de bibliotheken (op padnaam of via - -lname
) moet vermelden na de objectbestanden. Bij statische bibliotheken is de volgorde waarin ze zijn gespecificeerd van belang; bij gedeelde bibliotheken maakt de volgorde vaak niet uit.
Merk op dat op veel systemen, als u wiskundige functies gebruikt (van <math.h>
), u -lm
moet opgeven om de wiskundebibliotheek te laden - maar Mac OS X en macOS Sierra hebben dit niet nodig. Er zijn andere bibliotheken die afzonderlijke bibliotheken zijn op Linux en andere Unix-systemen, maar niet op macOS - POSIX-threads en POSIX realtime, en netwerkbibliotheken zijn voorbeelden. Bijgevolg varieert het koppelingsproces tussen platforms.
Andere compilatie-opties
Dit is alles wat u moet weten om te beginnen met het compileren van uw eigen C-programma's. Over het algemeen raden we u ook aan om de opdrachtregeloptie -Wall
te gebruiken:
% gcc -Wall -c foo.cc
De optie -Wall
zorgt ervoor dat de compiler u waarschuwt voor legale maar dubieuze -Wall
en helpt u heel veel bugs te vangen.
Als u wilt dat de compiler meer waarschuwingen naar u gooit (inclusief variabelen die zijn gedeclareerd maar niet worden gebruikt, vergeten een waarde terug te geven, enz.), Kunt u deze set opties gebruiken, omdat -Wall
, ondanks de naam, niet draait alle mogelijke waarschuwingen over:
% gcc -Wall -Wextra -Wfloat-equal -Wundef -Wcast-align -Wwrite-strings -Wlogical-op \
> -Wmissing-declarations -Wredundant-decls -Wshadow …
Merk op dat clang
heeft een optie -Weverything
die echt wordt ingeschakeld alle waarschuwingen in clang
.
Bestand types
Voor het compileren van C-programma's moet u met vijf soorten bestanden werken:
Bronbestanden : deze bestanden bevatten functiedefinities en hebben namen die volgens conventie op
.c
eindigen. Opmerking:.cc
en.cpp
zijn C ++ -bestanden; geen C-bestanden.
bijv.foo.c
Koptekstbestanden : deze bestanden bevatten functieprototypes en verschillende pre-processorafschriften (zie hieronder). Ze worden gebruikt om broncodebestanden toegang te geven tot extern gedefinieerde functies. Koptekstbestanden eindigen volgens afspraak op
.h
.
bijv.foo.h
Objectbestanden : deze bestanden worden geproduceerd als de uitvoer van de compiler. Ze bestaan uit functiedefinities in binaire vorm, maar ze kunnen niet op zichzelf worden uitgevoerd. Objectbestanden eindigen volgens de conventie in
.o
, hoewel ze op sommige besturingssystemen (bijv. Windows, MS-DOS) vaak eindigen op.obj
.
bijv.foo.o
foo.obj
Binaire uitvoerbare bestanden : deze worden geproduceerd als de uitvoer van een programma dat een "linker" wordt genoemd. De linker verbindt een aantal objectbestanden om een binair bestand te produceren dat direct kan worden uitgevoerd. Binaire uitvoerbare bestanden hebben geen speciaal achtervoegsel op Unix-besturingssystemen, hoewel ze meestal eindigen op
.exe
op Windows.
bijv.foo
foo.exe
Bibliotheken : een bibliotheek is een gecompileerd binair bestand, maar is op zichzelf geen uitvoerbaar bestand (er is dus geen
main()
functie in een bibliotheek). Een bibliotheek bevat functies die door meer dan één programma kunnen worden gebruikt. Een bibliotheek moet worden geleverd met header-bestanden die prototypes bevatten voor alle functies in de bibliotheek; naar deze header-bestanden moet worden verwezen (bijvoorbeeld;#include <library.h>
) in elk bronbestand dat de bibliotheek gebruikt. De linker moet dan worden doorverwezen naar de bibliotheek, zodat het programma met succes kan worden gecompileerd. Er zijn twee soorten bibliotheken: statisch en dynamisch.- Statische bibliotheek : een statische bibliotheek (
.a
bestanden voor POSIX-systemen en.lib
bestanden voor Windows - niet te verwarren met DLL-importbibliotheekbestanden , die ook de.lib
extensie gebruiken) is statisch ingebouwd in het programma. Statische bibliotheken hebben het voordeel dat het programma precies weet welke versie van een bibliotheek wordt gebruikt. Anderzijds zijn de uitvoerbare groottes groter omdat alle gebruikte bibliotheekfuncties zijn inbegrepen.
bijv.libfoo.a
foo.lib
- Dynamische bibliotheek : een dynamische bibliotheek (
.so
bestanden voor de meeste POSIX-systemen,.dylib
voor OSX en.dll
bestanden voor Windows) wordt tijdens runtime dynamisch gekoppeld door het programma. Dit worden soms ook gedeelde bibliotheken genoemd omdat één bibliotheekafbeelding door veel programma's kan worden gedeeld. Dynamische bibliotheken hebben het voordeel dat ze minder schijfruimte innemen als meer dan één toepassing de bibliotheek gebruikt. Ze staan ook bibliotheekupdates toe (bugfixes) zonder uitvoerbare bestanden opnieuw te moeten opbouwen.
bijv.foo.so
foo.dylib
foo.dll
- Statische bibliotheek : een statische bibliotheek (
De voorverwerker
Voordat de C-compiler begint met het compileren van een broncodebestand, wordt het bestand verwerkt in een voorbewerkingsfase. Deze fase kan worden uitgevoerd door een afzonderlijk programma of volledig worden geïntegreerd in één uitvoerbaar bestand. In elk geval wordt het automatisch aangeroepen door de compiler voordat de eigenlijke compilatie begint. De voorbewerkingsfase converteert uw broncode naar een andere broncode of vertaaleenheid door tekstuele vervangingen toe te passen. Je kunt het zien als een "aangepaste" of "uitgebreide" broncode. Die uitgebreide bron kan bestaan als een echt bestand in het bestandssysteem, of het kan slechts kort in het geheugen worden opgeslagen voordat het verder wordt verwerkt.
Preprocessor-opdrachten beginnen met het hekje ("#"). Er zijn verschillende preprocessoropdrachten; twee van de belangrijkste zijn:
Definieert :
#define
wordt voornamelijk gebruikt om constanten te definiëren. Bijvoorbeeld,#define BIGNUM 1000000 int a = BIGNUM;
wordt
int a = 1000000;
#define
wordt op deze manier gebruikt om te voorkomen dat op een aantal verschillende plaatsen in een broncodebestand expliciet een constante waarde moet worden weggeschreven. Dit is belangrijk voor het geval u later de constante waarde moet wijzigen; het is veel minder gevoelig voor fouten om het in de#define
eenmaal te wijzigen dan om het op meerdere plaatsen te moeten wijzigen, verspreid over de code.Omdat
#define
alleen geavanceerd zoeken en vervangen doet, kunt u ook macro's declareren. Bijvoorbeeld:#define ISTRUE(stm) do{stm = stm ? 1 : 0;}while(0) // in the function: a = x; ISTRUE(a);
wordt:
// in the function: a = x; do { a = a ? 1 : 0; } while(0);
Bij de eerste benadering is dit effect ongeveer hetzelfde als bij inline-functies, maar de preprocessor biedt geen typecontrole voor
#define
macro's. Het is bekend dat dit foutgevoelig is en het gebruik ervan vereist grote voorzichtigheid.Merk ook op dat de preprocessor ook opmerkingen zou vervangen door spaties, zoals hieronder wordt uitgelegd.
Omvat :
#include
wordt gebruikt om toegang te krijgen tot functiedefinities die buiten een broncodebestand zijn gedefinieerd. Bijvoorbeeld:#include <stdio.h>
zorgt ervoor dat de preprocessor de inhoud van
<stdio.h>
in het broncodebestand op de locatie van de#include
instructie plakt voordat deze wordt gecompileerd.#include
wordt bijna altijd gebruikt om header-bestanden op te nemen. Dit zijn bestanden die hoofdzakelijk functieverklaringen en#include
#define
instructies bevatten. In dit geval gebruiken we#include
om functies zoalsprintf
enscanf
te kunnen gebruiken, waarvan de verklaringen zich in het bestandstdio.h
. C-compilers staan u niet toe een functie te gebruiken tenzij deze eerder in dat bestand is aangegeven of gedefinieerd;#include
statements zijn dus de manier om eerder geschreven code in uw C-programma's opnieuw te gebruiken.Logische bewerkingen :
#if defined A || defined B variable = another_variable + 1; #else variable = another_variable * 2; #endif
zal worden gewijzigd in:
variable = another_variable + 1;
als A of B eerder in het project zijn gedefinieerd. Als dit niet het geval is, doet de preprocessor dit uiteraard:
variable = another_variable * 2;
Dit wordt vaak gebruikt voor code, die op verschillende systemen draait of op verschillende compilers compileert. Aangezien er globale definities zijn, die specifiek zijn voor de compiler / systeem, kun je deze testen en laat de compiler altijd gewoon de code gebruiken die hij zeker zal compileren.
Comments
De Preprocessor vervangt alle opmerkingen in het bronbestand door enkele spaties. Opmerkingen worden aangegeven met
//
tot het einde van de regel, of een combinatie van haakjes openen/*
en sluiten*/
opmerkingen.
De compiler
Nadat de C-processor alle kopbestanden heeft opgenomen en alle macro's heeft uitgebreid, kan de compiler het programma compileren. Het doet dit door de C-broncode om te zetten in een objectcodebestand, een bestand dat eindigt op .o
dat de binaire versie van de broncode bevat. Objectcode is echter niet direct uitvoerbaar. Om een uitvoerbaar bestand te maken, moet u ook code toevoegen voor alle bibliotheekfuncties die #include
omvatten d in het bestand (dit is niet hetzelfde als het opnemen van de verklaringen, wat #include
doet). Dit is de taak van de linker .
Over het algemeen hangt de exacte volgorde van het oproepen van een C-compiler sterk af van het systeem dat u gebruikt. Hier gebruiken we de GCC-compiler, hoewel er nog veel meer compilers bestaan:
% gcc -Wall -c foo.c
%
is de opdrachtprompt van het besturingssysteem. Dit vertelt de compiler om de pre-processor op het bestand foo.c
en deze vervolgens in het objectcodebestand foo.o
. De optie -c
betekent dat het broncodebestand in een objectbestand wordt gecompileerd, maar dat de linker niet wordt aangeroepen. Deze optie -c
is beschikbaar op POSIX-systemen, zoals Linux of macOS; andere systemen kunnen verschillende syntaxis gebruiken.
Als uw hele programma zich in één broncodebestand bevindt, kunt u dit in plaats daarvan doen:
% gcc -Wall foo.c -o foo
Dit vertelt de compiler om de pre-processor op foo.c
, te compileren en vervolgens te koppelen om een uitvoerbaar bestand genaamd foo
. De optie -o
geeft aan dat het volgende woord op de regel de naam is van het binaire uitvoerbare bestand (programma). Als u de -o
niet opgeeft (als u gewoon gcc foo.c
), wordt het uitvoerbare bestand om historische redenen a.out
genoemd.
Over het algemeen neemt de compiler vier stappen bij het converteren van een .c
bestand naar een uitvoerbaar bestand:
- voorbewerking - breidt tekst
#include
richtlijnen en#define
macro's in uw.c
bestand - compilatie - converteert het programma naar assemblage (u kunt de compiler bij deze stap stoppen door de optie
-S
toe te voegen) - assemblage - converteert de assemblage naar machinecode
- koppeling - koppelt de objectcode aan externe bibliotheken om een uitvoerbaar bestand te maken
Merk ook op dat de naam van de compiler die we gebruiken GCC is, wat staat voor zowel "GNU C compiler" als "GNU compiler collection", afhankelijk van de context. Andere C-compilers bestaan. Voor Unix-achtige besturingssystemen hebben veel van hen de naam cc
, voor "C compiler", wat vaak een symbolische link is naar een andere compiler. Op Linux-systemen is cc
vaak een alias voor GCC. Op macOS of OS-X wijst dit op clang.
De POSIX-standaard verplicht momenteel c99
als de naam van een C-compiler - het ondersteunt standaard de C99-standaard. Eerdere versies van POSIX verplichtten c89
als compiler. POSIX verplicht ook dat deze compiler de opties -c
en -o
begrijpt die we hierboven hebben gebruikt.
Opmerking: De optie -Wall
in beide gcc
voorbeelden vertelt de compiler om waarschuwingen af te drukken over dubieuze constructies, wat ten zeerste wordt aanbevolen. Het is ook een goed idee om andere waarschuwingsopties toe te voegen, bijvoorbeeld -Wextra
.
De vertaalfasen
Vanaf de C 2011-norm, vermeld in §5.1.1.2 Vertaalfasen , wordt de vertaling van broncode naar programmabeeld (bijvoorbeeld het uitvoerbare bestand) vermeld in 8 geordende stappen.
- De invoer van het bronbestand wordt toegewezen aan de brontekenset (indien nodig). Trigrafieën worden in deze stap vervangen.
- Vervolgregels (regels die eindigen op
\
) worden gesplitst met de volgende regel. - De broncode wordt ontleed in witruimte en voorverwerkingstokens.
- De preprocessor wordt toegepast, die richtlijnen uitvoert, macro's uitbreidt en pragma's toepast. Elk bronbestand aangetrokken door
#include
ondergaat vertaalfase 1 tot en met 4 (recursief indien nodig). Alle preprocessor-gerelateerde richtlijnen worden vervolgens verwijderd. - Waarden van brontekenset in tekenconstanten en tekenreeksliteralen worden toegewezen aan de uitvoeringstekenset.
- Stringliteralen naast elkaar worden aaneengeschakeld.
- De broncode wordt ontleed in tokens, die de vertaaleenheid vormen.
- Externe referenties worden opgelost en het programmabeeld wordt gevormd.
Een implementatie van een C-compiler kan verschillende stappen samen combineren, maar de resulterende afbeelding moet zich nog gedragen alsof de bovenstaande stappen afzonderlijk in de hierboven vermelde volgorde hadden plaatsgevonden.