Czy "inline" bez "static" lub "extern" jest kiedykolwiek przydatny w C99?

Kiedy próbuję zbudować ten kod

inline void f() {}

int main()
{
    f();
}

Użycie wiersza poleceń

gcc -std=c99 -o a a.c

I get a linker error(undefined reference to f). Błąd znika, jeśli używam static inline lub extern inline zamiast tylko inline, lub jeśli kompiluję z -O (więc funkcja jest faktycznie inlined).

Zachowanie to wydaje się być zdefiniowane w pkt 6.7.4 (6) normy C99:

Jeśli wszystkie deklaracje zakresu pliku dla funkcji w jednostce tłumaczeniowej zawierają inline Specyfikacja funkcji bez extern, wtedy definicja w tej jednostce translacji jest definicją inline. Definicja inline nie dostarcza definicji zewnętrznej dla funkcji i nie zabrania definicji zewnętrznej w innej jednostce tłumaczeniowej. Definicja inline stanowi alternatywę dla definicji zewnętrznej, której tłumacz może użyć do implementacji dowolnego wywołania funkcji w tej samej jednostce tłumaczeniowej. Nie jest określone, czy wywołanie funkcji używa definicji inline lub definicji zewnętrznej.

Jeśli dobrze to Rozumiem, Jednostka kompilacji z funkcją zdefiniowaną inline jak w powyższym przykładzie kompiluje się konsekwentnie tylko wtedy, gdy istnieje również funkcja zewnętrzna o tej samej nazwie, i nigdy nie wiem, czy wywołana jest moja własna funkcja lub funkcja zewnętrzna.

Czy to zachowanie nie jest całkowicie głupie? Czy w C99 warto zdefiniować funkcję inline bez static lub extern? Coś przeoczyłem?

Podsumowanie odpowiedzi

Oczywiście, że coś przeoczyłem, a zachowanie nie jest głupie. :)

Jak wyjaśnia Nemo , ideą jest umieszczenie definicji funkcji

inline void f() {}

W pliku nagłówkowym i tylko deklaracja

extern inline void f();

W odpowiednim .plik C. Tylko deklaracja extern uruchamia generowanie widocznego na zewnątrz kodu binarnego. I rzeczywiście nie ma zastosowania inline wplik c -- jest przydatny tylko w nagłówki.

Jak wyjaśnia uzasadnienie Komitetu C99 cytowane w odpowiedzi Jonathana, inline dotyczy optymalizacji kompilatora, która wymaga, aby definicja funkcji była widoczna w miejscu wywołania. Można to osiągnąć tylko poprzez umieszczenie definicji w nagłówku i oczywiście definicja w nagłówku nie może emitować kodu za każdym razem, gdy jest widziana przez kompilator. Ponieważ jednak kompilator nie jest zmuszony do wbudowania funkcji, definicja zewnętrzna musi istnieć gdzieś.

 88
Author: Community, 2011-06-11

3 answers

Faktycznie ta doskonała odpowiedź również odpowiada na twoje pytanie, myślę:

Extern inline

Chodzi o to, że "inline" może być użyty w pliku nagłówkowym, a następnie "extern inline" w pliku .plik C. "extern inline" jest po prostu tym, jak instruujesz kompilator, który plik obiektowy powinien zawierać (zewnętrznie widoczny) wygenerowany kod.

[update, do opracowania]

Wydaje mi się, że nie ma zastosowania "inline" (bez "static" lub "extern") w a .plik C. Ale w nagłówku plik ma sens i wymaga odpowiedniej deklaracji" extern inline " w niektórych .plik c do generowania samodzielnego kodu.

 36
Author: Nemo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:41

Od samej normy (ISO/IEC 9899:1999):

Dodatek J. 2 Nieokreślone Zachowanie

  • ...
  • funkcja z powiązaniem zewnętrznym jest deklarowana za pomocą specyfikacji funkcji inline, ale nie jest również zdefiniowana w tej samej jednostce translacyjnej (6.7.4).
  • ...

Komitet C99 napisał uzasadnienie i mówi:

6.7.4 specyfikatory funkcji

nowa funkcja C99: słowo kluczowe inline, zaadaptowane z C++, jest specyfikatorem funkcji , który może być używany tylko w deklaracjach funkcji. Jest przydatny do optymalizacji programów, które wymagają definicja funkcji, która ma być widoczna w miejscu wywołania. (Należy pamiętać, że Standard nie próbuje określić charakteru tych optymalizacji.)

Widoczność jest zapewniona, jeśli funkcja ma połączenie wewnętrzne, lub jeśli ma połączenie zewnętrzne i wywołanie jest w tej samej jednostce tłumaczeniowej co definicja zewnętrzna. W takich przypadkach obecność inline Słowo kluczowe w deklaracji lub definicji funkcji nie ma wpływu poza wskazaniem preferencja, że wywołania tej funkcji powinny być zoptymalizowane w stosunku do wywołań innych funkcji zadeklarowanych bez słowa kluczowego inline.

Widoczność jest problemem dla wywołania funkcji z linkiem zewnętrznym, gdzie wywołanie jest w inna jednostka translacji od definicji funkcji. W tym przypadku inline słowo kluczowe pozwala jednostce tłumaczeniowej zawierającej wywołanie również zawierać lokalną lub inline definicję funkcja.

Program może zawierać jednostkę tłumaczenia z zewnętrzną definicją, jednostkę tłumaczenia z inline definition, oraz jednostka translacji z deklaracją, ale bez definicji funkcji. Połączenia w tym drugim tłumaczeniu jednostka będzie używać definicji zewnętrznej jak zwykle.

Inline definicja funkcji jest uważana za inna definicja niż zewnętrzna definicja. Jeśli wywołanie jakiejś funkcji {[7] } z linkiem zewnętrznym występuje, gdy inline definicja jest widoczna, zachowanie jest takie samo, jak gdyby wywołanie zostało wykonane do innej funkcji, powiedzmy __func, z wewnętrznym połączeniem. Zgodny program nie może zależeć od tego, która funkcja jest dzwoniłem. Jest to model inline w standardzie.

Zgodny program nie może polegać na implementacji wykorzystującej definicję inline, ani nie może polega na realizacja z wykorzystaniem definicji zewnętrznej. Adres funkcji jest zawsze adresem odpowiadającym definicji zewnętrznej, ale gdy adres ten jest używany do wywołania funkcja, można użyć definicji inline. Dlatego poniższy przykład może nie zachowuj się zgodnie z oczekiwaniami.

inline const char *saddr(void)
{
    static const char name[] = "saddr";
    return name;
}
int compare_name(void)
{
    return saddr() == saddr(); // unspecified behavior
}

Ponieważ implementacja może użyć definicji inline dla jednego z wywołań do saddr i użyć zewnętrzna definicja drugiej, operacja równości nie jest gwarantowana do oceny do 1 (prawda). Pokazuje to, że obiekty statyczne zdefiniowane w definicji inline różnią się od swoich odpowiedni obiekt w definicji zewnętrznej. Motywowało to ograniczenie nawet definiowanie obiektu nie - const tego typu.

Inlining został dodany do standardu w taki sposób, że można go zaimplementować za pomocą istniejącego linkera technologii, a podzbiór C99 jest kompatybilny z C++. Udało się to osiągnąć, wymagając dokładnie jednego tłumaczenia Jednostka zawierająca definicję funkcji inline be określona jako ta, która dostarcza zewnętrznej definicji funkcji. Ponieważ to Specyfikacja składa się po prostu z deklaracji, która albo nie zawiera słowa kluczowego inline, albo zawiera zarówno inline, jak i extern, zostanie również zaakceptowany przez tłumacza C++.

Inlining w C99 rozszerza specyfikację C++ na dwa sposoby. Po pierwsze, jeśli funkcja jest zadeklarowana inline w jednej jednostce tłumaczeniowej nie musi być deklarowana inline w każda inna jednostka tłumaczeniowa. Pozwala to na przykład na funkcję biblioteki, która ma być wbudowana w bibliotekę, ale dostępna tylko przez zewnętrzną definicję gdzie indziej. Alternatywą użycia funkcji wrapper dla funkcja zewnętrzna wymaga dodatkowej nazwy; może również mieć negatywny wpływ na wydajność jeśli Tłumacz nie wykonuje podstawienia w wierszu.

Po Drugie, wymóg, aby wszystkie definicje funkcji inline były "dokładnie takie same" jest zastąpione wymogiem, że zachowanie programu nie powinno zależeć od tego, czy wywołanie jest zaimplementowane z widoczną definicją inline lub definicją zewnętrzną funkcji. Pozwala to na wyspecjalizowanie definicji inline do jej użycia w ramach określonej jednostki tłumaczeniowej. Na przykład, zewnętrzna definicja funkcji bibliotecznej może zawierać pewne walidacje argumentów, które nie są potrzebne do wywołań z innych funkcji w tej samej bibliotece. Te rozszerzenia oferują niektórych zalet; oraz programistów, którzy troszczą się o kompatybilność może po prostu przestrzegać surowszych zasad C++.

Zauważ, że nie jest to odpowiednie dla implementacji, aby zapewnić inline definicje standardu funkcje biblioteki w standardowych nagłówkach, ponieważ może to złamać niektóre starsze kody, które ponownie deklarują standardowe funkcje biblioteki po włączeniu ich nagłówków. Słowo kluczowe inline to przeznaczone wyłącznie do zapewnienia użytkownikom przenośnego sposobu sugerowania funkcje. Ponieważ standardowe nagłówki nie muszą być przenośne, implementacje mają inne opcje w linii:

#define abs(x) __builtin_abs(x)

Lub inne przenośne mechanizmy do wbudowywania standardowych funkcji bibliotecznych.

 23
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-11 06:11:06

> I get a linker error (undefined reference to f)

Działa tutaj: Linux x86-64, GCC 4.1.2. Może to być błąd w Twoim kompilatorze; nie widzę w cytowanym akapicie niczego ze standardu, który zabraniałby danego programu. Zwróć uwagę na użycie if zamiast MFF.

Definicja inline zapewnia alternatywę dla definicji zewnętrznej, której Tłumacz może użyć do zaimplementowania dowolnego wywołania funkcji w ta sama jednostka tłumaczeniowa.

Tak więc, jeśli znasz zachowanie funkcji f i chcesz wywołać ją w ciasnej pętli, możesz skopiować i wkleić jej definicję do modułu, aby zapobiec wywołaniu funkcji; lub, możesz podać definicję, która dla celów bieżącego modułu jest równoważna(ale pomija walidację wejściową lub jakąkolwiek optymalizację możesz sobie wyobrazić). Kompilator ma jednak możliwość optymalizacji rozmiaru programu.

 0
Author: Fred Foo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-10 23:26:29