Indbygget funktion - Inline function
I C og C ++ programmeringssprog , en indbygget funktion er et kvalificeret med nøgleordet inline ; dette tjener to formål. For det første fungerer det som et kompileringsdirektiv, der foreslår (men ikke kræver), at kompilatoren erstatter kroppens funktion inline ved at udføre inline -udvidelse , dvs. ved at indsætte funktionskoden på adressen for hvert funktionsopkald og derved spare overhead på et funktionsopkald. I denne henseende er det analogt med register lagerklasse -specificatoren , som på samme måde giver et optimeringshint. Det andet formål med inlineer at ændre koblingsadfærd; detaljerne i dette er komplicerede. Dette er nødvendigt på grund af C/C ++ - separat kompilering+ koblingsmodel, især fordi funktionens definition (brødtekst) skal duplikeres i alle oversættelsesenheder, hvor den bruges, for at tillade inlining under kompilering , hvilket, hvis funktionen har ekstern sammenkobling , forårsager en kollision under forbindelse (det krænker det unikke ved eksterne symboler). C og C ++ (og dialekter som GNU C og Visual C ++) løser dette på forskellige måder.
Eksempel
En inlinefunktion kan skrives i C eller C ++ sådan:
inline void swap(int *m, int *n)
{
int tmp = *m;
*m = *n;
*n = tmp;
}
Derefter en erklæring som følgende:
swap(&x, &y);
kan oversættes til (hvis kompilatoren beslutter at foretage inlining, hvilket typisk kræver, at optimering er aktiveret):
int tmp = x;
x = y;
y = tmp;
Når du implementerer en sorteringsalgoritme, der laver masser af swaps, kan dette øge eksekveringshastigheden.
Standard support
C ++ og C99 , men ikke forgængerne K&R C og C89 , har understøttelse af inlinefunktioner, dog med forskellige semantikker. I begge tilfælde inlinetvinges ikke inlining; kompilatoren kan frit vælge ikke at inline funktionen overhovedet, eller kun i nogle tilfælde. Forskellige kompilatorer varierer i, hvor kompleks en funktion de kan klare at inline. Mainstream C ++ - kompilatorer som Microsoft Visual C ++ og GCC understøtter en mulighed, der lader kompilatorerne automatisk inline enhver passende funktion, også dem, der ikke er markeret som inlinefunktioner. Det er dog inlineikke muligt at udelade nøgleordet for at lade kompilatoren træffe alle inline -beslutninger, da linkeren derefter vil klage over dobbelte definitioner i forskellige oversættelsesenheder. Dette skyldes, at det inlineikke kun giver kompilatoren et tip om, at funktionen skal være inlinet, det har også en effekt på, om kompilatoren vil generere en opkaldelig out-of-line kopi af funktionen (se lagerklasser af inline-funktioner ).
Ikke -standard udvidelser
GNU C , som en del af dialekten gnu89, som den tilbyder, har understøttelse af inlinesom en udvidelse til C89. Semantikken adskiller sig imidlertid fra både C ++ og C99. armcc i C90-tilstand tilbyder også inlinesom en ikke-standardudvidelse, med semantik forskellig fra gnu89 og C99.
Nogle implementeringer giver et middel til at tvinge kompilatoren til at inline en funktion, normalt ved hjælp af implementeringsspecifikke deklarationsspecifikatorer:
- Microsoft Visual C ++:
__forceinline - gcc eller clang:
__attribute__((always_inline))eller__attribute__((__always_inline__)), hvis sidstnævnte er nyttig til at undgå en konflikt med en brugerdefineret makro ved navnalways_inline.
Ukritisk anvendelse af det kan resultere i større kode (oppustet eksekverbar fil), minimal eller ingen præstationsgevinst og i nogle tilfælde endda tab af ydeevne. Desuden kan kompilatoren ikke inline funktionen under alle omstændigheder, selv når inlining er tvunget; i dette tilfælde genererer både gcc og Visual C ++ advarsler.
Tvinge inlining er nyttig, hvis
-
inlineikke respekteres af kompilatoren (ignoreres af compiler cost/benefit -analysator) og - inlining resulterer i et nødvendigt præstationsboost
For kodeportabilitet kan følgende preprocessordirektiver bruges:
#ifdef _MSC_VER
#define forceinline __forceinline
#elif defined(__GNUC__)
#define forceinline inline __attribute__((__always_inline__))
#elif defined(__CLANG__)
#if __has_attribute(__always_inline__)
#define forceinline inline __attribute__((__always_inline__))
#else
#define forceinline inline
#endif
#else
#define forceinline inline
#endif
Lagerklasser af inline -funktioner
static inlinehar de samme effekter i alle C -dialekter og C ++. Den udsender om nødvendigt en lokalt synlig (out-of-line kopi af) funktionen.
Uanset lagerklassen kan kompilatoren ignorere inlinekvalifikatoren og generere et funktionsopkald i alle C -dialekter og C ++.
Effekten af lagerklassen, externnår den anvendes eller ikke anvendes på inlinefunktioner, er forskellig mellem C -dialekterne og C ++.
C99
I C99 udsender en defineret funktion inlinealdrig, og en defineret funktion extern inlinevil altid udsende en eksternt synlig funktion. I modsætning til i C ++ er der ingen måde at bede om, at en eksternt synlig funktion, der deles mellem oversættelsesenheder, kun udsendes, hvis det kræves.
Hvis inlineerklæringer blandes med extern inlineerklæringer eller med ikke -kvalificerede erklæringer (dvs. uden inlinekvalifikation eller lagerklasse), skal oversættelsesenheden indeholde en definition (uanset om den er ukvalificeret inlineeller extern inline, og der udsendes en eksternt synlig funktion for den.
En defineret funktion inlinekræver præcis en funktion med det navn et andet sted i programmet, som enten er defineret extern inlineeller uden kvalifikation. Hvis der findes mere end én sådan definition i hele programmet, vil linkeren klage over dublerede symboler. Hvis den imidlertid mangler, klager linkeren ikke nødvendigvis, for hvis alle anvendelser kunne skrånes, er det ikke nødvendigt. Men det kan klage, da kompilatoren altid kan ignorere inlinekvalifikatoren og i stedet generere opkald til funktionen, som det typisk sker, hvis koden kompileres uden optimering. (Dette kan være den ønskede adfærd, hvis funktionen på alle måder skal skrives ind overalt, og der skal genereres en fejl, hvis den ikke er det.) En bekvem måde er at definere inlinefunktionerne i overskriftsfiler og oprette en .c -fil pr. funktion, der indeholder en extern inlinedeklaration for den og inklusive den respektive headerfil med definitionen. Det er ligegyldigt, om erklæringen er før eller efter inkluderingen.
For at forhindre, at utilgængelig kode tilføjes til den endelige eksekverbare, hvis alle anvendelser af en funktion blev indskrænket, tilrådes det at sætte objektfilerne for alle sådanne .c -filer med en enkelt extern inlinefunktion i en statisk biblioteksfil , typisk med ar rcsog derefter linke mod det bibliotek i stedet for de enkelte objektfiler. Det får kun de objektfiler til at blive linket, der faktisk er nødvendige, i modsætning til at linke objektfilerne direkte, hvilket får dem til altid at blive inkluderet i den eksekverbare. Biblioteksfilen skal dog angives efter alle de andre objektfiler på linkerkommandolinjen, da opkald fra objektfiler, der er angivet efter bibliotekfilen til funktionerne, ikke vil blive behandlet af linkeren. Opkald fra inlinefunktioner til andre inlinefunktioner løses automatisk af linkeren ( sindstillingen i ar rcssikrer dette).
En alternativ løsning er at bruge linktidsoptimering i stedet for et bibliotek. gcc giver flaget -Wl,--gc-sectionstil at udelade sektioner, hvor alle funktioner er ubrugte. Dette vil være tilfældet for objektfiler, der indeholder koden for en enkelt ubrugt extern inlinefunktion. Det fjerner imidlertid også alle andre ubrugte sektioner fra alle andre objektfiler, ikke kun dem, der er relateret til ubrugte extern inlinefunktioner. (Det kan være ønskeligt at knytte funktioner til den eksekverbare fil, der skal kaldes af programmøren fra debuggeren frem for af selve programmet, f.eks. For at undersøge programmets interne tilstand.) Med denne tilgang er det også muligt at bruge en enkelt .c -fil med alle extern inlinefunktioner i stedet for en .c -fil pr. funktion. Derefter skal filen kompileres med -fdata-sections -ffunction-sections. Imidlertid advarer gcc -manualsiden om det og siger "Brug kun disse muligheder, når der er betydelige fordele ved at gøre det."
Nogle anbefaler en helt anden tilgang, nemlig at definere funktioner som i static inlinestedet for inlinei header -filer. Derefter genereres ingen kode, der ikke kan nås. Denne tilgang har imidlertid en ulempe i det modsatte tilfælde: Duplikatkode vil blive genereret, hvis funktionen ikke kunne skrives i mere end én oversættelsesenhed. Den udsendte funktionskode kan ikke deles mellem oversættelsesenheder, fordi den skal have forskellige adresser. Dette er en anden ulempe; at tage adressen på en sådan funktion defineret som static inlinei en overskriftsfil vil give forskellige værdier i forskellige oversættelsesenheder. Derfor bør static inlinefunktioner kun bruges, hvis de kun bruges i én oversættelsesenhed, hvilket betyder, at de kun skal gå til den respektive .c -fil, ikke til en headerfil.
gnu89
gnu89 semantik for inlineog extern inlineer i det væsentlige det stik modsatte af dem i C99, med den undtagelse, at gnu89 tillader omdefinering af en extern inlinefunktion som en ukvalificeret funktion, mens C99 inlineikke gør det. Således er gnu89 extern inlineuden omdefinering ligesom C99 inline, og gnu89 inlineer som C99 extern inline; med andre ord, i gnu89 vil en defineret funktion inlinealtid og en defineret funktion extern inlinealdrig udsende en eksternt synlig funktion. Begrundelsen for dette er, at den matcher variabler, som lagring aldrig vil blive reserveret til, hvis den defineres som externog altid, hvis den er defineret uden. Begrundelsen for C99 er derimod, at det ville være forbløffende, hvis brug inlineville have en bivirkning-altid at udsende en ikke-inline version af funktionen-det er i modstrid med hvad navnet antyder.
Bemærkningerne til C99 om behovet for at tilvejebringe nøjagtigt en eksternt synlig funktionsinstans for inlinerede funktioner og om det resulterende problem med utilgængelig kode gælder også mutatis mutandis for gnu89.
gcc til og med version 4.2 brugte gnu89 inlinesemantik, selv når det -std=c99blev udtrykkeligt angivet. Med version 5 skiftede gcc fra gnu89 til gnu11 dialekten, hvilket effektivt muliggjorde C99 inlinesemantik som standard. For at bruge gnu89 semantik i stedet skal de aktiveres eksplicit, enten med -std=gnu89eller, for kun at påvirke inlining -fgnu89-inline, eller ved at tilføje gnu_inlineattributten til alle inlinedeklarationer. At sikre C99 semantik, enten -std=c99, -std=c11, -std=gnu99eller -std=gnu11(uden -fgnu89-inlinekan anvendes).
C ++
I C ++ vil en defineret funktion inlineom nødvendigt udsende en funktion, der deles mellem oversættelsesenheder, typisk ved at sætte den i den fælles sektion af objektfilen, som den er nødvendig for. Funktionen skal have den samme definition overalt, altid med inlinekvalifikatoren. I C ++ extern inlineer det samme som inline. Begrundelsen for C ++ tilgangen er, at det er den mest bekvemme måde for programmereren, da der ikke skal træffes særlige forholdsregler for fjernelse af utilgængelig kode, og ligesom for almindelige funktioner gør det ingen forskel, om det externer specificeret eller ej.
Den inlinekvalifikationstager automatisk sat til en funktion defineret som del af en klasse definition.
armcc
armcc i C90 -tilstand giver extern inlineog inlinesemantik, der er de samme som i C ++: Sådanne definitioner udsender en funktion, der deles mellem oversættelsesenheder, hvis det kræves. I C99 -tilstand extern inlineudsender den altid en funktion, men ligesom i C ++ deles den mellem oversættelsesenheder. Den samme funktion kan således defineres extern inlinei forskellige oversættelsesenheder. Dette matcher den traditionelle adfærd hos Unix C-kompilatorer for flere ikke- externdefinitioner af uinitialiserede globale variabler.
Begrænsninger
At tage adressen til en inlinefunktion kræver kode for at en ikke-skråtstillet kopi af denne funktion under alle omstændigheder skal udsendes.
I C99 må en inlineeller extern inlinefunktion ikke få adgang til staticglobale variabler eller definere ikke- const staticlokale variabler. const staticlokale variabler kan være forskellige objekter i forskellige oversættelsesenheder, afhængigt af om funktionen blev inlineret, eller om der blev foretaget et opkald. Kun static inlinedefinitioner kan referere til identifikatorer med intern forbindelse uden begrænsninger; det vil være forskellige objekter i hver oversættelsesenhed. I C ++ er både constog ikke- const staticlokale tilladt, og de refererer til det samme objekt i alle oversættelsesenheder.
gcc kan ikke inline funktioner, hvis
- de er forskellige ,
- brug
alloca - brug beregnet
goto - brug ikke -lokal
goto - bruge indlejrede funktioner
- brug
setjmp - brug
__builtin_longjmp - brug
__builtin_return, eller - brug
__builtin_apply_args
Baseret på Microsoft -specifikationer på MSDN kan MS Visual C ++ ikke inline (ikke engang med __forceinline), hvis
- Funktionen eller den, der ringer op, kompileres med /Ob0 (standardindstillingen for fejlfindingsbygninger).
- Funktionen og den, der ringer op, bruger forskellige former for undtagelseshåndtering (C ++ undtagelseshåndtering i den ene, struktureret undtagelseshåndtering i den anden).
- Funktionen har en variabel argumentliste .
- Funktionen bruger inline -samling , medmindre den er kompileret med /Og, /Ox, /O1 eller /O2.
- Funktionen er rekursiv og ledsages ikke af
#pragma inline_recursion(on). Med pragmaet skrives rekursive funktioner til en standarddybde på 16 opkald. Brug pragma for at reducereinline_depthinliningdybden. - Funktionen er virtuel og kaldes virtuelt. Direkte opkald til virtuelle funktioner kan skrives ind.
- Programmet tager funktionens adresse, og opkaldet foretages via markøren til funktionen. Direkte opkald til funktioner, der har fået deres adresse taget, kan skrives ind.
- Funktionen er også markeret med den nøgne
__declspecmodifikator.
Problemer
Udover problemerne med inline -udvidelse generelt (se Inline -udvidelse § Effekt på ydeevne ) er inlinefunktioner som sprogfunktion muligvis ikke så værdifulde som de ser ud af flere årsager:
- Ofte er en kompilator i en bedre position end et menneske til at beslutte, om en bestemt funktion skal skråstilles. Nogle gange kan kompilatoren muligvis ikke inline så mange funktioner som programmereren angiver.
- Et vigtigt punkt at bemærke er, at koden (for
inlinefunktionen) bliver udsat for sin klient (den kaldende funktion). - Efterhånden som funktioner udvikler sig, kan de blive egnede til inlining, hvor de ikke var før, eller ikke længere egnede til inlining, hvor de var før. Selvom inlining eller un-inlining en funktion er lettere end at konvertere til og fra makroer, kræver det stadig ekstra vedligeholdelse, hvilket typisk giver relativt lidt fordel.
- Inline-funktioner, der bruges til spredning i native C-baserede kompilationssystemer, kan øge kompileringstiden, da den mellemliggende repræsentation af deres kroppe kopieres til hvert opkaldssted.
- Specifikationen
inlinei C99 kræver præcis en ekstern definition af funktionen, hvis den bruges et eller andet sted. Hvis en sådan definition ikke blev leveret af programmøren, kan det let føre til linkerfejl. Dette kan ske med optimering slået fra, hvilket typisk forhindrer inlining. Tilføjelse af definitionerne kan derimod forårsage utilgængelig kode, hvis programmøren ikke omhyggeligt undgår det ved at lægge dem i et bibliotek til at linke, ved hjælp af optimering af linktid ellerstatic inline. - I C ++ er det nødvendigt at definere en
inlinefunktion i hvert modul (oversættelsesenhed), der bruger den, hvorimod en almindelig funktion kun må defineres i et enkelt modul. Ellers ville det ikke være muligt at kompilere et enkelt modul uafhængigt af alle andre moduler. Afhængigt af kompilatoren kan dette medføre, at hver respektive objektfil indeholder en kopi af funktionens kode, for hvert modul med en vis brug, der ikke kunne skrånes. - I indlejret software skal visse funktioner ofte placeres i visse kodesektioner ved hjælp af specielle kompilerinstruktioner, f.eks. "Pragma" -udsagn. Nogle gange skal en funktion i et hukommelsessegment muligvis kalde en funktion i et andet hukommelsessegment, og hvis inlining af den kaldte funktion forekommer, kan koden for den kaldte funktion ende i et segment, hvor det ikke burde være. For eksempel kan hukommelsessegmenter med høj ydeevne være meget begrænset i kodeplads, og hvis en funktion, der hører til i et sådant rum, kalder en anden stor funktion, der ikke er beregnet til at være i sektionen med høj ydeevne, og den kaldte funktion bliver upassende skråtstillet, så dette kan forårsage, at hukommelsessegmentet med høj ydeevne løber tør for kodeplads. Af denne grund er det nogle gange nødvendigt at sikre, at funktioner ikke bliver inline.
Citater
- "En funktionserklæring [ .... ] Med en inline -specifikator erklærer en inline -funktion. Inline -specificatoren angiver til implementeringen, at inline -substitution af funktionsorganet på opkaldspunktet skal foretrækkes frem for den sædvanlige funktionsopkaldsmekanisme. En implementering er ikke påkrævet for at udføre denne inline -substitution på opkaldspunktet; selvom denne inline -substitution udelades, skal de andre regler for inline -funktioner, der er defineret i 7.1.2, dog stadig respekteres. "
- - ISO/IEC 14882: 2011, den nuværende C ++ - standard, afsnit 7.1.2
- "En funktion, der er erklæret med en inline- funktionsspecifikator, er en inline-funktion. [...] At gøre en funktion til en inline-funktion antyder, at opkald til funktionen skal være så hurtigt som muligt. I hvilket omfang sådanne forslag er effektive, er implementeringsdefineret ( fodnote: For eksempel udfører en implementering muligvis aldrig inline -substitution eller udfører kun inline -substitutioner til opkald inden for en inline -deklaration. )
- "[...] En inline definition giver ikke en ekstern definition for funktionen, og forbyder ikke en ekstern definition i et andet oversættelse enhed . En inline definition er et alternativ til en ekstern definition, som en oversætter kan anvende til at gennemføre nogen opkald til funktionen i den samme oversættelsesenhed. Det er uspecificeret, om et opkald til funktionen anvender inline -definitionen eller den eksterne definition. "
- - ISO 9899: 1999 (E), C99 -standarden, afsnit 6.7.4
Se også
Referencer
- JANA, DEBASISH (1. januar 2005). C ++ OG OBJEKTORIENTERET PROGRAMMERINGSPARADIGM . PHI Learning Pvt. Ltd. ISBN 978-81-203-2871-6.
- Sengupta, Probal (1. august 2004). Objektorienteret programmering: Fundamentals And Applications . PHI Learning Pvt. Ltd. ISBN 978-81-203-1258-6.
- Svenk, Goran (2003). Objektorienteret programmering: Brug af C ++ til teknik og teknologi . Cengage læring. ISBN 0-7668-3894-3.
- Balagurusamy (2013). Objektorienteret programmering med C ++ . Tata McGraw-Hill Education. ISBN 978-1-259-02993-6.
- Kirch-Prinz, Ulla; Prinz, Peter (2002). En komplet guide til programmering i C ++ . Jones & Bartlett Learning. ISBN 978-0-7637-1817-6.
- Conger, David (2006). Oprettelse af spil i C ++: En trinvis vejledning . Nye ryttere. ISBN 978-0-7357-1434-2.
- Skinner, MT (1992). Den avancerede C ++ - bog . Silicon Press. ISBN 978-0-929306-10-0.
- Kærlighed (1. september 2005). Linux Kerneudvikling . Pearson Education. ISBN 978-81-7758-910-8.
- DEHURI, SATCHIDANANDA; JAGADEV, ALOK KUMAR; RATH, AMIYA KUMAR (8. maj 2007). MÅLORIENTERET PROGRAMMERING VED AT BRUGE C ++ . PHI Learning Pvt. Ltd. ISBN 978-81-203-3085-6.
eksterne links
- Inline -funktioner med GNU Compiler Collection (GCC)
- Resumé af "inline" semantik i C og C ++ , af LLVM -bidragyder David Chisnall