C Language
Kompilering
Sök…
Introduktion
C-språket är traditionellt ett sammanställt språk (i motsats till tolkat). C Standard definierar översättningsfaser , och produkten av att tillämpa dem är en programbild (eller sammanställt program). I c11 listas faserna i §5.1.1.2.
Anmärkningar
Filnamnstillägg | Beskrivning |
---|---|
.c | Källfilen. Innehåller vanligtvis definitioner och kod. |
.h | Rubrikfil. Innehåller vanligtvis deklarationer. |
.o | Objektfil. Kompilerad kod på maskinspråk. |
.obj | Alternativ förlängning för objektfiler. |
.a | Biblioteksfil. Paket med objektfiler. |
.dll | Dynamic-Link Library på Windows. |
.so | Delat objekt (bibliotek) på många Unix-liknande system. |
.dylib | Dynamic-Link Library på OSX (Unix-variant). |
.exe , .com | Windows körbar fil. Bildas genom att länka objektfiler och biblioteksfiler. I Unix-liknande system finns det ingen speciell filnamnstillägg för körbara filer. |
POSIX c99-kompilatorflaggor | Beskrivning |
---|---|
-o filename | Utdatafilnamn t.ex. ( bin/program.exe , program ) |
-I directory | sök efter rubriker i direrctory . |
-D name | definiera name |
-L directory | sök efter bibliotek i directory . |
-l name | länk bibliotekets libname . |
Kompilatorer på POSIX-plattformar (Linux, mainframes, Mac) accepterar vanligtvis dessa alternativ, även om de inte kallas c99
.
GCC-flaggor (GNU Compiler Collection) | Beskrivning |
---|---|
-Wall | Aktiverar att alla varningsmeddelanden som vanligtvis accepteras är användbara. |
-Wextra | Aktiverar fler varningsmeddelanden, kan vara för högljudda. |
-pedantic | Tvinga varningar där koden bryter mot den valda standarden. |
-Wconversion | Aktivera varningar om implicit konvertering, använd försiktigt. |
-c | Samlar källfiler utan att länka. |
-v | Skriver ut sammanställningsinformation. |
-
gcc
accepterar POSIX-flaggorna plus många andra. - Många andra kompilatorer på POSIX-plattformar (
clang
, leverantörspecifika kompilatorer) använder också de flaggor som listas ovan. - Se även Anropa GCC för många fler alternativ.
TCC (Tiny C Compiler) -flaggor | Beskrivning |
---|---|
-Wimplicit-function-declaration | Varna för implicit funktionsdeklaration. |
-Wunsupported | Varna för GCC-funktioner som inte stöds som ignoreras av TCC. |
-Wwrite-strings | Gör strängkonstanter till typen const char * istället för char *. |
-Werror | Avbryt sammanställning om varningar utfärdas. |
-Wall | Aktivera alla varningar, utom -Werror , -Wunusupported och -Wwrite strings . |
Länken
Länkarens uppgift är att koppla ihop ett gäng objektfiler ( .o
filer) till en binär körbar. Processen att länka innebär huvudsakligen att lösa symboliska adresser till numeriska adresser . Resultatet av länkprocessen är normalt ett körbart program.
Under länkprocessen hämtar länken alla objektmoduler som anges på kommandoraden, lägger till någon systemspecifik startkod framför och försöker lösa alla externa referenser i objektmodulen med externa definitioner i andra objektfiler (objektfiler kan anges direkt på kommandoraden eller implicit kan läggas till via bibliotek). Den tilldelar sedan belastningsadresser för objektfilerna, det vill säga den anger var koden och data hamnar i adressutrymmet för det färdiga programmet. När den har fått belastningsadresserna kan den ersätta alla symboliska adresser i objektkoden med "riktiga", numeriska adresser i målets adressutrymme. Programmet är redo att köras nu.
Detta inkluderar både objektfiler som kompilatorn skapade från dina källkodfiler såväl som objektfiler som har kompilerats för dig och samlats in i biblioteksfiler. Dessa filer har namn som slutar i .a
eller .so
, och du behöver normalt inte veta om dem, eftersom länken vet var de flesta av dem finns och kommer att länka dem automatiskt efter behov.
Implicit åkallande av länken
Liksom förprocessorn är länken ett separat program, ofta kallad ld
(men Linux använder till exempel collect2
). Liksom förprocessorn anropas länken automatiskt åt dig när du använder kompilatorn. Således är det normala sättet att använda länken som följer:
% gcc foo.o bar.o baz.o -o myprog
Denna rad berättar kompilatorn att länka samman tre objektfiler ( foo.o
, bar.o
och baz.o
) till en binär körbar fil med namnet myprog
. Nu har du en fil som heter myprog
som du kan köra och som förhoppningsvis kommer att göra något coolt och / eller användbart.
Explicit åkallande av länken
Det är möjligt att åberopa länken direkt, men detta är sällan tillrådligt och är vanligtvis mycket plattformsspecifikt. Det vill säga alternativ som fungerar på Linux fungerar inte nödvändigtvis på Solaris, AIX, macOS, Windows och på liknande sätt för någon annan plattform. Om du arbetar med GCC kan du använda gcc -v
att se vad som körs för dina räkning.
Alternativ för länken
Länken tar också några argument för att ändra dess beteende. Följande kommando skulle säga gcc att länka foo.o
och bar.o
, men inkluderar också ncurses
biblioteket.
% gcc foo.o bar.o -o foo -lncurses
Detta är faktiskt (mer eller mindre) motsvarande
% gcc foo.o bar.o /usr/lib/libncurses.so -o foo
(även om libncurses.so
kan vara libncurses.a
, som bara är ett arkiv skapat med ar
). Observera att du bör lista upp biblioteken (antingen efter sökvägsnamn eller via -lname
) efter objektfilerna. För statiska bibliotek är ordningen att de är specificerade viktiga; ofta, med delade bibliotek, spelar ordningen ingen roll.
Observera att på många system, om du använder matematiska funktioner (från <math.h>
), måste du ange -lm
att ladda matematikbiblioteket - men Mac OS X och macOS Sierra behöver inte detta. Det finns andra bibliotek som är separata bibliotek på Linux och andra Unix-system, men inte på macOS - POSIX-trådar och POSIX realtid och nätverksbibliotek är exempel. Följaktligen varierar länkprocessen mellan plattformar.
Andra sammanställningsalternativ
Detta är allt du behöver veta för att börja sammanställa dina egna C-program. Generellt rekommenderar vi också att du använder kommandoradsalternativet -Wall
:
% gcc -Wall -c foo.cc
-Wall
får kompilatorn att varna dig om lagliga men tvivelaktiga kodkonstruktioner och hjälper dig att fånga många buggar mycket tidigt.
Om du vill att kompilatorn ska kasta fler varningar på dig (inklusive variabler som deklareras men inte används, glömmer att returnera ett värde etc.) kan du använda den här uppsättningen alternativ, eftersom -Wall
, trots namnet, inte vänder alla möjliga varningar om:
% gcc -Wall -Wextra -Wfloat-equal -Wundef -Wcast-align -Wwrite-strings -Wlogical-op \
> -Wmissing-declarations -Wredundant-decls -Wshadow …
Observera att clang
har ett alternativ - -Weverything
som verkligen -Weverything
alla varningar i clang
.
Filtyper
Att sammanställa C-program kräver att du arbetar med fem typer av filer:
Källfiler : Dessa filer innehåller funktionsdefinitioner och har namn som slutar i
.c
enligt konvention. Obs:.cc
och.cpp
är C ++ -filer; inte C-filer.
t.ex.foo.c
Rubrikfiler : Dessa filer innehåller funktionsprototyper och olika uttalanden från förprocessorn (se nedan). De används för att tillåta källkodfiler att få åtkomst till externt definierade funktioner. Headerfiler slutar i
.h
av konvention.
t.ex.foo.h
Objektfiler : Dessa filer produceras som utgången från kompilatorn. De består av funktionsdefinitioner i binär form, men de kan inte köras av sig själva. Objektfiler slutar i
.o
genom konvention, även om de på vissa operativsystem (t.ex. Windows, MS-DOS) slutar ofta i.obj
.
t.ex.foo.o
foo.obj
Binära körbara filer : Dessa produceras som utgången från ett program som kallas "länkare". Länken länkar samman ett antal objektfiler för att producera en binär fil som kan köras direkt. Binära körbara filer har inget speciellt suffix på Unix-operativsystem, även om de i allmänhet slutar i
.exe
på Windows.
t.ex.foo
foo.exe
Bibliotek : Ett bibliotek är en sammanställd binär men är i sig inte en körbar (dvs. det finns ingen huvudfunktion
main()
i ett bibliotek). Ett bibliotek innehåller funktioner som kan användas av mer än ett program. Ett bibliotek ska levereras med rubrikfiler som innehåller prototyper för alla funktioner i biblioteket; dessa sidhuvudfiler bör refereras (t.ex.#include <library.h>
) i alla källfiler som använder biblioteket. Länken behöver sedan hänvisas till biblioteket så att programmet framgångsrikt kan kompileras. Det finns två typer av bibliotek: statisk och dynamisk.- Statisk bibliotek : Ett statiskt bibliotek (
.a
filer för POSIX-system och.lib
filer för Windows - inte att förväxla med DLL-importbibliotekfiler , som också använder.lib
) är statiskt inbyggt i programmet. Statiska bibliotek har fördelen att programmet vet exakt vilken version av ett bibliotek som används. Å andra sidan är storleken på körbara filer större eftersom alla använda biblioteksfunktioner ingår.
t.ex.libfoo.a
foo.lib
- Dynamiskt bibliotek : Ett dynamiskt bibliotek (
.so
filer för de flesta POSIX-system,.dylib
för OSX och.dll
filer för Windows) är dynamiskt länkat under körning av programmet. Dessa kallas också ibland delade bibliotek eftersom en biblioteksbild kan delas av många program. Dynamiska bibliotek har fördelen att ta mindre diskutrymme om mer än ett program använder biblioteket. De tillåter också biblioteksuppdateringar (bug fixes) utan att behöva bygga om körbara filer.
t.ex.foo.so
foo.dylib
foo.dll
- Statisk bibliotek : Ett statiskt bibliotek (
Förprocessorn
Innan C-kompilatorn börjar kompilera en källkodefil behandlas filen i en förbehandlingsfas. Denna fas kan utföras av ett separat program eller vara helt integrerat i en körbar. I alla fall åberopas den automatiskt av kompilatorn innan rätt kompilering börjar. Förbehandlingsfasen konverterar din källkod till en annan källkod eller översättningsenhet genom att använda textutbyten. Du kan tänka på det som en "modifierad" eller "utvidgad" källkod. Den utvidgade källan kan existera som en riktig fil i filsystemet, eller den kan bara lagras i minnet under en kort tid innan den behandlas vidare.
Preprocessor-kommandon börjar med pundtecknet ("#"). Det finns flera preprocessorkommandon; två av de viktigaste är:
Definierar :
#define
används främst för att definiera konstanter. Till exempel,#define BIGNUM 1000000 int a = BIGNUM;
blir
int a = 1000000;
#define
används på detta sätt för att undvika att uttryckligen skriva ut något konstant värde på många olika platser i en källkodefil. Detta är viktigt om du behöver ändra det konstanta värdet senare; det är mycket mindre felbenäget att ändra det en gång, i#define
, än att behöva ändra det på flera platser spridda över hela koden.Eftersom
#define
bara gör avancerad sökning och ersätter kan du också förklara makron. Till exempel:#define ISTRUE(stm) do{stm = stm ? 1 : 0;}while(0) // in the function: a = x; ISTRUE(a);
blir:
// in the function: a = x; do { a = a ? 1 : 0; } while(0);
Vid första tillnärmningen är denna effekt ungefär densamma som med inline-funktioner, men förbehandlaren ger inte typkontroll för
#define
makron. Detta är välkänt för att vara felaktigt och deras användning kräver stor försiktighet.Observera också här att förbehandlaren också skulle ersätta kommentarer med ett tomt utrymme som förklaras nedan.
Inkluderar :
#include
används för att komma åt funktionsdefinitioner definierade utanför en källkodefil. Till exempel:#include <stdio.h>
får preprocessorn att klistra in innehållet i
<stdio.h>
i källkodfilen på platsen för#include
uttalandet innan det sammanställs.#include
används nästan alltid för att inkludera rubrikfiler, som är filer som huvudsakligen innehåller funktionsdeklarationer och#define
uttalanden. I det här fallet använder vi#include
för att kunna använda funktioner somprintf
ochscanf
, vars förklaringar finns i filenstdio.h
. C-kompilatorer tillåter dig inte att använda en funktion såvida den inte tidigare har deklarerats eller definierats i den filen;#include
uttalanden är alltså sättet att återanvända tidigare skrivna koder i dina C-program.Logiska operationer :
#if defined A || defined B variable = another_variable + 1; #else variable = another_variable * 2; #endif
kommer att ändras till:
variable = another_variable + 1;
om A eller B definierades någonstans i projektet tidigare. Om detta inte är fallet kommer naturligtvis förbehandlaren att göra detta:
variable = another_variable * 2;
Detta används ofta för kod som körs på olika system eller kompilerar på olika kompilatorer. Eftersom det finns globala definieringar, som är kompilator / systemspecifika, kan du testa på dessa definierar och alltid låta kompilatorn bara använda koden han kommer att sammanställa säkert.
kommentarer
Förprocessorn ersätter alla kommentarer i källfilen med enstaka mellanslag. Kommentarer indikeras av
//
upp till slutet av raden, eller en kombination av öppning/*
och stängning*/
kommentar parentes.
Kompilatorn
Efter att C-förprocessorn har inkluderat alla rubrikfiler och utvidgat alla makron kan kompilatorn kompilera programmet. Det gör detta genom att förvandla C-källkoden till en objektkodfil, som är en fil som slutar i .o
som innehåller den binära versionen av källkoden. Objektkod kan dock inte direkt köras. För att göra en körbar måste du också lägga till kod för alla biblioteksfunktioner som var #include
d i filen (detta är inte detsamma som att inkludera deklarationerna, vilket är vad #include
gör). Detta är en uppgift för länk .
I allmänhet beror den exakta sekvensen för hur man påkallar en C-kompilator mycket av systemet du använder. Här använder vi GCC-kompilatorn, men det bör noteras att det finns många fler kompilatorer:
% gcc -Wall -c foo.c
%
är OS: s kommandotolk. Detta säger kompilatorn att köra foo.c
på filen foo.c
och sedan sammanställa den till objektkodefilen foo.o
Alternativet -c
betyder att kompilera källkodefilen till en objektfil men inte att åberopa länken. Det här alternativet -c
är tillgängligt på POSIX-system, t.ex. Linux eller macOS; andra system kan använda olika syntax.
Om hela programmet finns i en källkodfil kan du istället göra detta:
% gcc -Wall foo.c -o foo
Detta säger kompilatorn att köra foo.c
på foo.c
, kompilera den och sedan länka den för att skapa en körbar kallad foo
. Alternativet -o
anger att nästa ord på raden är namnet på den binära körbara filen (programmet). Om du inte anger -o
, (om du bara skriver gcc foo.c
) kommer den körbara namnet a.out
av historiska skäl.
Generellt tar kompilatorn fyra steg när man konverterar en .c
fil till en körbar:
- förbehandling - utvidgar text
#include
#define
direktiv och#define
makron i din.c
fil - kompilering - konverterar programmet till montering (du kan stoppa kompilatorn i detta steg genom att lägga till
-S
alternativet) - montering - konverterar enheten till maskinkod
- koppling - länkar objektkoden till externa bibliotek för att skapa en körbar
Observera också att namnet på kompilatorn vi använder är GCC, som står för både "GNU C-kompilator" och "GNU-kompilatorsamling", beroende på sammanhang. Andra C-kompilatorer finns. För Unix-liknande operativsystem har många av dem namnet cc
, för "C-kompilator", som ofta är en symbolisk länk till någon annan kompilator. På Linux-system är cc
ofta ett alias för GCC. På macOS eller OS-X pekar det på klang.
POSIX-standarderna c99
närvarande c99
som namnet på en C-kompilator - den stöder C99-standarden som standard. Tidigare versioner av POSIX krävde c89
som kompilatorn. POSIX kräver också att denna kompilator förstår alternativen -c
och -o
som vi använde ovan.
Obs: -Wall
finns i båda gcc
exemplen berättar kompilatorn att skriva ut varningar om tvivelaktiga konstruktioner, vilket rekommenderas starkt. Det är också bra att lägga till andra varningsalternativ , t.ex. -Wextra
.
Översättningsfaserna
Från C 2011-standarden, listad i §5.1.1.2 Översättningsfaser , är översättningen av källkoden till programbilden (t.ex. körbar) listad för att uppstå i åtta ordnade steg.
- Källfilsingången mappas till källteckenuppsättningen (vid behov). Trigrafer ersätts i detta steg.
- Fortsättningsrader (linjer som slutar med
\
) splitsas med nästa rad. - Källkoden delas in i whitespace och preprocessing tokens.
- Förprocessorn tillämpas, som kör direktiven, expanderar makron och tillämpar pragmas. Varje källfil som dras in av
#include
genomgår översättningsfas 1 till 4 (rekursivt vid behov). Alla förbehandlingsrelaterade direktiv raderas sedan. - Källteckenuppsättningsvärden i teckenkonstanter och strängbokstäver mappas till exekveringsteckenuppsättningen.
- Stränglitteraler intill varandra är sammankopplade.
- Källkoden delas in i tokens, som innefattar översättningsenheten.
- Externa referenser löses och programbilden bildas.
En implementering av en C-kompilator kan kombinera flera steg tillsammans, men den resulterande bilden måste fortfarande fungera som om ovanstående steg hade skett separat i den ordning som anges ovan.