Kiedy próbuję zbudować ten kod
inline void f() {}
int main()
{
f();
}
za pomocą wiersza poleceń
gcc -std=c99 -o a a.c
Otrzymuję błąd konsolidatora (niezdefiniowane odniesienie do f
). Błąd znika, jeśli używam static inline
lub extern inline
zamiast po prostu inline
, lub jeśli kompiluję z -O
(więc funkcja jest faktycznie wbudowana).
Wydaje się, że zachowanie to jest zdefiniowane w pkt 6.7.4 (6) normy C99:
Jeśli wszystkie deklaracje zakresu plików dla funkcji w jednostce translacji zawierają specyfikator
inline
funkcji bezextern
, to definicja w tej jednostce translacji jest definicją wbudowaną. Definicja wbudowana nie zapewnia zewnętrznej definicji funkcji i nie zabrania umieszczania zewnętrznej definicji w innej jednostce tłumaczeniowej. Definicja wbudowana stanowi alternatywę dla definicji zewnętrznej, której tłumacz może użyć do zaimplementowania dowolnego wywołania funkcji w tej samej jednostce tłumaczeniowej. Nie jest określone, czy wywołanie funkcji używa definicji wbudowanej, czy definicji zewnętrznej.
Jeśli wszystko dobrze 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ływana jest moja własna funkcja, czy funkcja zewnętrzna.
Czy to zachowanie nie jest całkowicie głupie? Czy kiedykolwiek warto zdefiniować funkcję inline
bez static
lub extern
w C99? Czy coś mi brakuje?
Podsumowanie odpowiedzi
Oczywiście czegoś mi brakowało, a zachowanie nie jest głupie. :)
Jak wyjaśnia Nemo , chodzi o to, aby umieścić definicję funkcji
inline void f() {}
w pliku nagłówkowym i tylko deklaracja
extern inline void f();
w odpowiednim pliku c. Tylko extern
deklaracja wyzwala generowanie widocznego na zewnątrz kodu binarnego. I rzeczywiście nie ma zastosowania inline
w pliku .c - jest użyteczne tylko w nagłówkach.
Jak wyjaśnia uzasadnienie komitetu C99 przytoczone w odpowiedzi Jonathana , inline
chodzi o optymalizacje kompilatora, które wymagają, 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 widziany przez kompilator. Ale ponieważ kompilator nie jest zmuszony do faktycznego wstawiania funkcji, musi gdzieś istnieć zewnętrzna definicja.
inline
bezstatic
iextern
, chociaż. Niestety, żadna z tych kwestii nie została poruszona w tym pytaniu.Odpowiedzi:
Właściwie ta doskonała odpowiedź również odpowiada na twoje pytanie, myślę:
Co robi extern inline?
Pomysł jest taki, że „inline” może być użyte w pliku nagłówkowym, a następnie „extern inline” w pliku .c. „extern inline” to po prostu sposób, w jaki instruujesz kompilator, który plik obiektowy powinien zawierać (widoczny z zewnątrz) wygenerowany kod.
[aktualizacja, rozwinięcie]
Wydaje mi się, że w pliku .c nie ma pożytku z wyrażenia „inline” (bez „static” lub „extern”). Ale w pliku nagłówkowym ma to sens i wymaga odpowiedniej deklaracji „extern inline” w jakimś pliku .c, aby faktycznie wygenerować samodzielny kod.
źródło
inline void f() {}
w nagłówku iextern inline void f();
w pliku .c? Czyli rzeczywista definicja funkcji jest umieszczana w nagłówku, a plik .c zawiera w tym przypadku zwykłą deklarację, w odwrotnej kolejności?inline
bezstatic
lubextern
. Oczywiściestatic inline
jest w porządku, ale nie o to chodzi w tym pytaniu i odpowiedzi.-std=c99
zamiast-std=gnu89
.Z samej normy (ISO / IEC 9899: 1999):
Komitet C99 napisał uzasadnienie i mówi:
źródło
> Pojawia się błąd konsolidatora (niezdefiniowane odniesienie dof
)Działa tutaj: Linux x86-64, GCC 4.1.2. Może to być błąd w twoim kompilatorze; Nie widzę w cytowanym akapicie ze standardu niczego, co zabrania danego programu. Zwróć uwagę na użycie if zamiast iff .Tak więc, jeśli znasz zachowanie funkcji
f
i chcesz wywołać ją w ścisłej pętli, możesz skopiować i wkleić jej definicję do modułu, aby zapobiec wywołaniom funkcji; lub możesz podać definicję, która na potrzeby bieżącego modułu jest równoważna (ale pomija sprawdzanie poprawności danych wejściowych lub dowolną optymalizację, jaką możesz sobie wyobrazić). Twórca kompilatora ma jednak zamiast tego opcję optymalizacji pod kątem rozmiaru programu.źródło