Inline funkce - Inline function
V programovacích jazycích C a C ++ je vložená funkce kvalifikovaná pomocí klíčového slova ; to slouží dvěma účelům. Za prvé slouží jako směrnice kompilátoru, která naznačuje (ale nevyžaduje), aby kompilátor nahradil tělo vložené funkce provedením vloženého rozšíření , tj. Vložením kódu funkce na adresu každého volání funkce, čímž se ušetří režie volání funkce. V tomto ohledu je analogický se specifikátorem třídy úložiště , který podobně poskytuje nápovědu k optimalizaci. Druhým účelem je změnit chování propojení; detaily jsou komplikované. To je nutné vzhledem k C/C ++ separátnímu modelu kompilace+ propojení, konkrétně proto, že definice (tělo) funkce musí být duplikována ve všech překladových jednotkách, kde je použita, aby bylo možné při kompilaci vkládat , což, pokud má funkce externí propojení , způsobí během propojení kolizi (narušuje jedinečnost vnějších symbolů). C a C ++ (a dialekty jako GNU C a Visual C ++) to řeší různými způsoby.
inline
register
inline
Příklad
inline
Funkce může být napsán v C nebo C ++ takto:
inline void swap(int *m, int *n)
{
int tmp = *m;
*m = *n;
*n = tmp;
}
Potom prohlášení, jako je následující:
swap(&x, &y);
lze přeložit do (pokud se kompilátor rozhodne provést vložení, které obvykle vyžaduje povolení optimalizace):
int tmp = x;
x = y;
y = tmp;
Když implementujete řadicí algoritmus, který dělá spoustu swapů, může to zvýšit rychlost provádění.
Standardní podpora
C ++ a C99 , ale ne jeho předchůdci K&R C a C89 , mají podporu inline
funkcí, i když s jinou sémantikou. V obou případech inline
nevynucuje vložení; kompilátor se může svobodně rozhodnout, že funkci nebude vkládat vůbec, nebo jen v některých případech. Různé překladače se liší v tom, jak složitou funkci mohou spravovat vložené. Tradiční kompilátory C ++, jako jsou Microsoft Visual C ++ a GCC, podporují možnost, která umožňuje kompilátorům automaticky vkládat jakoukoli vhodnou funkci, dokonce i ty, které nejsou označeny jako inline
funkce. Jednoduše vynechání inline
klíčového slova, aby kompilátor mohl provádět veškerá rozhodnutí o vložení, není možné, protože linker si pak bude stěžovat na duplicitní definice v různých překladových jednotkách. Důvodem je, že inline
nejen dává kompilátoru nápovědu, že by měla být funkce vložená, ale má také vliv na to, zda kompilátor vygeneruje kopii funkce s možností volání mimo řádek (viz třídy úložiště vložených funkcí ).
Nestandardní rozšíření
GNU C , jako součást dialektu gnu89, který nabízí, má podporu inline
jako rozšíření C89. Sémantika se však liší od C ++ a C99. armcc v režimu C90 také nabízí inline
jako nestandardní rozšíření se sémantikou odlišnou od gnu89 a C99.
Některé implementace poskytují prostředky k vynucení kompilátoru pro vložení funkce, obvykle pomocí specifikátorů deklarace specifických pro implementaci:
- Microsoft Visual C ++:
__forceinline
- gcc nebo clang:
__attribute__((always_inline))
nebo__attribute__((__always_inline__))
, který je užitečný, aby se předešlo konfliktu s uživatelem definovaným makrem pojmenovanýmalways_inline
.
Nerozlišené použití může mít za následek větší kód (nafouklý spustitelný soubor), minimální nebo žádný nárůst výkonu a v některých případech dokonce ztrátu výkonu. Kompilátor navíc nemůže za všech okolností vkládat funkci, i když je vložení vynucené; v tomto případě generují varování gcc i Visual C ++.
Vynucení inliningu je užitečné, pokud
-
inline
není kompilátorem respektován (ignorován analyzátorem nákladů a přínosů kompilátoru) a - inlining vede k nezbytnému zvýšení výkonu
Pro přenositelnost kódu lze použít následující směrnice preprocesoru:
#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
Třídy úložiště vložených funkcí
static inline
má stejné efekty ve všech C dialektech a C ++. V případě potřeby vydá místně viditelnou (out-of-line kopii) funkce.
Bez ohledu na třídu úložiště může kompilátor ignorovat inline
kvalifikátor a generovat volání funkce ve všech dialektech C a C ++.
Efekt třídy úložiště extern
při použití nebo nepoužití na inline
funkce se mezi dialekty C a C ++ liší.
C99
V C99 inline
bude definovaná funkce nikdy a definovaná funkce extern inline
bude vždy vysílat externě viditelnou funkci. Na rozdíl od C ++ neexistuje žádný způsob, jak požádat o externě viditelnou funkci sdílenou mezi překladovými jednotkami, aby byla vydávána pouze v případě potřeby.
Pokud inline
jsou deklarace smíchány s extern inline
deklaracemi nebo s nekvalifikovanými deklaracemi (tj. Bez inline
kvalifikátoru nebo třídy úložiště), překladová jednotka musí obsahovat definici (bez ohledu na to, zda je nekvalifikovaná inline
, nebo extern inline
) a bude pro ni vydána externě viditelná funkce.
Definovaná funkce inline
vyžaduje přesně jednu funkci s tímto názvem někde jinde v programu, která je definována extern inline
nebo bez kvalifikátoru. Pokud je v celém programu k dispozici více než jedna taková definice, bude linker stěžovat na duplicitní symboly. Pokud však chybí, linker si nemusí nutně stěžovat, protože pokud by bylo možné naznačit všechna použití, není to nutné. Může si ale stěžovat, protože kompilátor může inline
kvalifikátor vždy ignorovat a místo toho generovat volání funkce, jak se obvykle stává, pokud je kód kompilován bez optimalizace. (To může být požadované chování, pokud se předpokládá, že je tato funkce všemi způsoby podtržena, a pokud tomu tak není, měla by být vygenerována chyba.) Pohodlným způsobem je definovat inline
funkce v hlavičkových souborech a vytvořit jeden soubor .c na funkci, obsahující extern inline
deklaraci a včetně příslušného hlavičkového souboru s definicí. Nezáleží na tom, zda je deklarace před nebo za zahrnutím.
Aby se zabránilo přidání nedosažitelného kódu do konečného spustitelného souboru, pokud by byla vložena všechna použití funkce, doporučujeme umístit objektové soubory všech takových souborů .c s jedinou extern inline
funkcí do souboru statické knihovny , typicky s ar rcs
, pak propojit proti že knihovna namísto souborů jednotlivých objektů. To způsobí propojení pouze těch souborů objektů, které jsou skutečně potřebné, na rozdíl od přímého propojení souborů objektů, což způsobí, že budou vždy zahrnuty do spustitelného souboru. Soubor knihovny však musí být zadán po všech ostatních souborech objektů na příkazovém řádku linkeru, protože linker nebude zvažovat volání ze souborů objektů zadaných po souboru knihovny do funkcí. Hovory z inline
funkcí do jiných inline
funkcí budou automaticky vyřešeny linkerem ( s
možnost v ar rcs
zajišťuje toto).
Alternativním řešením je použít optimalizaci času propojení místo knihovny. gcc poskytuje příznak -Wl,--gc-sections
pro vynechání sekcí, ve kterých jsou všechny funkce nepoužité. To bude případ objektových souborů obsahujících kód jedné nepoužívané extern inline
funkce. Odebere však také všechny ostatní nepoužité sekce ze všech ostatních souborů objektů, nejen z těch, které souvisejí s nepoužívanými extern inline
funkcemi. (Může být žádoucí propojit funkce do spustitelného souboru, které mají být vyvolány programátorem z debuggeru, nikoli samotným programem, např. Pro zkoumání vnitřního stavu programu.) S tímto přístupem je také možné použít jeden soubor .c se všemi extern inline
funkcemi místo jednoho souboru .c na funkci. Poté musí být soubor zkompilován pomocí -fdata-sections -ffunction-sections
. Stránka manuálu gcc na to však upozorňuje slovy: „Tyto možnosti používejte pouze tehdy, jsou -li z toho významné výhody“.
Někteří doporučují zcela odlišný přístup, kterým je definovat funkce jako static inline
místo inline
v hlavičkových souborech. Poté nebude vygenerován žádný nedostupný kód. Tento přístup má však v opačném případě nevýhodu: Duplicitní kód bude vygenerován, pokud funkci nelze vložit do více než jedné překladové jednotky. Kód emitované funkce nelze sdílet mezi překladovými jednotkami, protože musí mít různé adresy. To je další nevýhoda; převzetí adresy takové funkce definované static inline
v souboru záhlaví přinese různé hodnoty v různých překladových jednotkách. static inline
Funkce by proto měly být použity pouze tehdy, pokud jsou použity pouze v jedné překladové jednotce, což znamená, že by měly jít pouze do příslušného souboru .c, nikoli do souboru záhlaví.
gnu89
gnu89 sémantika inline
a extern inline
jsou v podstatě pravým opakem těch v C99, s výjimkou, že gnu89 umožňuje předefinování extern inline
funkce jako nekvalifikované funkce, zatímco C99 inline
ne. Gnu89 extern inline
bez redefinice je tedy jako C99 inline
a gnu89 inline
je jako C99 extern inline
; jinými slovy, v gnu89 inline
bude definovaná funkce vždy a definovaná funkce extern inline
nikdy nevydávat externě viditelnou funkci. Důvodem je to, že odpovídá proměnným, pro které nebude úložiště nikdy rezervováno, pokud je definováno jako extern
a vždy, pokud je definováno bez. Důvodem pro C99 je naopak to, že by bylo úžasné, kdyby použití inline
mělo vedlejší účinek-vždy vyzařovat neinlinovanou verzi funkce-což je v rozporu s tím, co naznačuje její název.
Poznámky pro C99 o potřebě poskytnout přesně jednu externě viditelnou instanci funkce pro vložené funkce a o výsledném problému s nedosažitelným kódem platí obdobně i pro gnu89.
gcc až do verze 4.2 včetně používala inline
sémantiku gnu89, i když -std=c99
byla výslovně uvedena. Ve verzi 5 gcc přešlo z gnu89 na gnu11 dialekt, což inline
ve výchozím nastavení účinně umožňuje sémantiku C99 . Chcete -li místo toho použít sémantiku gnu89, musí být povolena explicitně, buď s -std=gnu89
nebo, aby ovlivnila pouze vložení -fgnu89-inline
, nebo přidáním gnu_inline
atributu ke všem inline
deklaracím. Pro zajištění C99 sémantiku, a to buď -std=c99
, -std=c11
, -std=gnu99
nebo -std=gnu11
(bez -fgnu89-inline
lze použít).
C ++
V C ++ inline
bude definovaná funkce v případě potřeby vysílat funkci sdílenou mezi překladovými jednotkami, obvykle vložením do společné části souboru objektu, pro který je potřeba. Funkce musí mít všude stejnou definici, vždy s inline
kvalifikátorem. V C ++ extern inline
je stejné jako inline
. Důvodem přístupu C ++ je, že je to pro programátora nejpohodlnější způsob, protože není nutné provádět žádná zvláštní opatření pro eliminaci nedosažitelného kódu a stejně jako u běžných funkcí nezáleží na tom, zda extern
je zadán či nikoli.
inline
Kvalifikátor se automaticky přidá do funkce definované v rámci definice třídy.
armcc
armcc v režimu C90 poskytuje extern inline
a inline
sémantiku, která jsou stejná jako v C ++: Takové definice budou v případě potřeby vysílat funkci sdílenou mezi překladovými jednotkami. V režimu C99 extern inline
vždy vydává funkci, ale stejně jako v C ++ bude sdílena mezi překladovými jednotkami. Stejnou funkci lze tedy definovat extern inline
v různých translačních jednotkách. To odpovídá tradičnímu chování kompilátorů Unixu C pro více extern
nedefinic neinicializovaných globálních proměnných.
Omezení
Převzetí adresy inline
funkce vyžaduje v každém případě kód pro neinlinovanou kopii této funkce.
V C99 funkce an inline
nebo extern inline
nesmí přistupovat ke static
globálním proměnným ani definovat nelokální const
static
proměnné. const static
místní proměnné mohou, ale nemusí být různé objekty v různých překladových jednotkách, v závislosti na tom, zda byla funkce vložena nebo zda bylo provedeno volání. Pouze static inline
definice mohou odkazovat na identifikátory s interním propojením bez omezení; budou to různé objekty v každé překladové jednotce. V C ++ jsou povoleni oba const
i ne- const
static
místní obyvatelé a odkazují na stejný objekt ve všech překladových jednotkách.
gcc nemůže vkládat funkce, pokud
- jsou variadičtí ,
- použití
alloca
- použít vypočítaný
goto
- použít nelokální
goto
- používat vnořené funkce
- použití
setjmp
- použití
__builtin_longjmp
- použít
__builtin_return
, popř - použití
__builtin_apply_args
Na základě specifikací Microsoft na MSDN nemůže MS Visual C ++ vkládat (ani s __forceinline
), pokud
- Funkce nebo její volající je zkompilován s /Ob0 (výchozí volba pro sestavení ladění).
- Funkce a volající používají různé typy zpracování výjimek ( zpracování výjimek C ++ v jednom, zpracování strukturovaných výjimek v druhém).
- Funkce má seznam proměnných argumentů .
- Funkce používá vložené sestavení , pokud není kompilováno s /Og, /Ox, /O1 nebo /O2.
- Funkce je rekurzivní a není doprovázena
#pragma inline_recursion(on)
. S pragma jsou rekurzivní funkce vloženy do výchozí hloubky 16 hovorů. Chcete -li snížit hloubku vložení, použijteinline_depth
pragma. - Funkce je virtuální a nazývá se virtuálně. Přímá volání virtuálních funkcí mohou být vložena.
- Program převezme adresu funkce a volání se uskuteční přes ukazatel na funkci. Přímá volání funkcí, jejichž adresa byla přijata, mohou být podtržena.
- Funkce je také označena nahým
__declspec
modifikátorem.
Problémy
Kromě problémů s inline expanzí obecně (viz Inline expanze § Vliv na výkon ) inline
nemusí být funkce jako jazyková funkce tak cenné, jak se zdá, a to z řady důvodů:
- Kompilátor je často v lepší pozici než člověk, aby se rozhodl, zda má být konkrétní funkce vložena. Někdy kompilátor nemusí být schopen vložit tolik funkcí, jak programátor uvádí.
- Důležitým bodem, který je třeba poznamenat, je, že kód (
inline
funkce) se vystaví svému klientovi (volající funkce). - Jak se funkce vyvíjejí, mohou se stát vhodnými pro inlining tam, kde dříve nebyly, nebo již nejsou vhodné pro inlining tam, kde byly dříve. I když je vložení nebo zrušení vložení funkce jednodušší než převod do az makra, stále vyžaduje zvláštní údržbu, která obvykle přináší relativně malý užitek.
- Vložené funkce používané při šíření v nativních kompilačních systémech na bázi C mohou prodloužit dobu kompilace, protože do každého místa volání se zkopíruje přechodná reprezentace jejich těl.
- Specifikace
inline
v C99 vyžaduje přesně jednu externí definici funkce, pokud je někde použita. Pokud takovou definici programátor neposkytl, může to snadno vést k chybám linkeru. To se může stát při vypnuté optimalizaci, která obvykle brání vložení. Přidání definic na druhé straně může způsobit nedostupnost kódu, pokud se mu programátor pečlivě nevyhne, vložením do knihovny pro propojení, pomocí optimalizace času propojení, popřstatic inline
. - V C ++ je nutné definovat
inline
funkci v každém modulu (překladové jednotce), který ji používá, zatímco běžná funkce musí být definována pouze v jednom modulu. Jinak by nebylo možné sestavit jeden modul nezávisle na všech ostatních modulech. V závislosti na kompilátoru to může způsobit, že každý příslušný objektový soubor bude obsahovat kopii kódu funkce pro každý modul s určitým použitím, které nelze vložit. - V integrovaném softwaru je často nutné určité funkce umístit do určitých sekcí kódu pomocí speciálních instrukcí kompilátoru, jako jsou příkazy „pragma“. Někdy může funkce v jednom paměťovém segmentu vyžadovat volání funkce v jiném paměťovém segmentu, a pokud dojde k vložení volané funkce, pak kód volané funkce může skončit v segmentu, kde by neměl být. Například vysoce výkonné paměťové segmenty mohou být v kódovém prostoru velmi omezené, a pokud funkce patřící do takového prostoru volá jinou velkou funkci, která není určena k tomu, aby byla v sekci s vysokým výkonem, a volaná funkce se nevhodně vloží, pak to může způsobit, že segmentu vysoce výkonné paměti dojde místo v kódu. Z tohoto důvodu je někdy nutné zajistit, aby funkce nebyly vloženy.
Citáty
- „S deklaraci funkce [...] Inline specifikátor deklaruje vložené funkce. Čím inline specifikátor ukazuje na realizaci, že inline substituce funkce těla v místě volání se upřednostňuje, aby obvyklým mechanismem volání funkce. Implementace není vyžadováno k provedení této vložené náhrady v místě volání; nicméně i když je tato vložená náhrada vynechána, ostatní pravidla pro vložené funkce definované v 7.1.2 budou stále respektována. “
- - ISO/IEC 14882: 2011, aktuální norma C ++, část 7.1.2
- „Funkce deklarovaná se specifikátorem vložené funkce je vložená funkce. [. poznámka pod čarou: Například implementace nemusí nikdy provádět vložené nahrazování nebo může provádět pouze vložené nahrazování volání v rozsahu vložené deklarace. )
- „[...] Definice Inline neposkytuje externí definici pro funkci, a nezakazuje externí definice v jiné jednotce překlad . Definice Inline poskytuje alternativu k externímu definice, která překládá se může použít pro realizaci kteréhokoliv volání funkce ve stejné překladové jednotce. Není určeno, zda volání funkce používá vloženou definici nebo externí definici. "
- - ISO 9899: 1999 (E), norma C99, oddíl 6.7.4
Viz také
Reference
- JANA, DEBASISH (1. ledna 2005). PROGRAMOVACÍ PARADIGMA V OBLASTI C ++ A OBJEKTU . PHI Learning Pvt. Ltd. ISBN 978-81-203-2871-6.
- Sengupta, Probal (1. srpna 2004). Objektově orientované programování: Základy a aplikace . PHI Learning Pvt. Ltd. ISBN 978-81-203-1258-6.
- Svenk, Goran (2003). Objektově orientované programování: Použití C ++ pro inženýrství a technologie . Cengage Learning. ISBN 0-7668-3894-3.
- Balagurusamy (2013). Objektově orientované programování v C ++ . Vzdělávání Tata McGraw-Hill. ISBN 978-1-259-02993-6.
- Kirch-Prinz, Ulla; Prinz, Peter (2002). Kompletní průvodce programováním v C ++ . Jones & Bartlett Learning. ISBN 978-0-7637-1817-6.
- Conger, David (2006). Vytváření her v C ++: Podrobný průvodce . Noví jezdci. ISBN 978-0-7357-1434-2.
- Skinner, MT (1992). Pokročilá kniha C ++ . Silicon Press. ISBN 978-0-929306-10-0.
- Láska (1. září 2005). Vývoj jádra Linuxu . Pearsonovo vzdělávání. ISBN 978-81-7758-910-8.
- DEHURI, SATCHIDANANDA; JAGADEV, ALOK KUMAR; RATH, AMIYA KUMAR (8. května 2007). OBJEKTOVĚ PROGRAMOVÁNÍ PODLE C ++ . PHI Learning Pvt. Ltd. ISBN 978-81-203-3085-6.
externí odkazy
- Vložené funkce s kolekcí kompilátorů GNU (GCC)
- Shrnutí "vložené" sémantiky v C a C ++ , přispěvatel LLVM David Chisnall