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 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:

  1. 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

  2. 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

  3. 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

  4. 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

  5. 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

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:

  1. 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.

  2. 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 som printf och scanf , vars förklaringar finns i filen stdio.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.

  3. 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.

  4. 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.cfoo.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:

  1. förbehandling - utvidgar text #include #define direktiv och #define makron i din .c fil
  2. kompilering - konverterar programmet till montering (du kan stoppa kompilatorn i detta steg genom att lägga till -S alternativet)
  3. montering - konverterar enheten till maskinkod
  4. 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.

  1. Källfilsingången mappas till källteckenuppsättningen (vid behov). Trigrafer ersätts i detta steg.
  2. Fortsättningsrader (linjer som slutar med \ ) splitsas med nästa rad.
  3. Källkoden delas in i whitespace och preprocessing tokens.
  4. 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.
  5. Källteckenuppsättningsvärden i teckenkonstanter och strängbokstäver mappas till exekveringsteckenuppsättningen.
  6. Stränglitteraler intill varandra är sammankopplade.
  7. Källkoden delas in i tokens, som innefattar översättningsenheten.
  8. 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.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow