W C nie zauważyłem żadnego efektu extern
słowa kluczowego użytego przed deklaracją funkcji. Na początku myślałem, że definiowanie extern int f();
w jednym pliku wymusza zaimplementowanie go poza zakresem pliku. Jednak dowiedziałem się, że oba:
extern int f();
int f() {return 0;}
i
extern int f() {return 0;}
kompiluje się dobrze, bez ostrzeżeń z gcc. Użyłem gcc -Wall -ansi
; nie zaakceptowałby nawet //
komentarzy.
Czy są jakieś efekty do użycia extern
przed definicją funkcji ? A może jest to tylko opcjonalne słowo kluczowe bez skutków ubocznych dla funkcji.
W tym drugim przypadku nie rozumiem, dlaczego projektanci standardu zdecydowali się zaśmiecać gramatykę zbędnymi słowami kluczowymi.
EDIT: Do wyjaśnienia, wiem, że korzystanie z Internetu do extern
zmiennych, ale pytam tylko extern
w funkcjach .
Odpowiedzi:
Mamy dwa pliki, foo.c i bar.c.
Tutaj jest foo.c
Teraz tutaj jest bar.c
Jak widać, nie mamy współdzielonego nagłówka między foo.c i bar.c, jednak bar.c wymaga czegoś zadeklarowanego w foo.c, gdy jest dowiązany, a foo.c potrzebuje funkcji z bar.c, gdy jest dowiązana.
Używając 'extern', mówisz kompilatorowi, że cokolwiek po nim następuje, zostanie znalezione (niestatyczne) w czasie łączenia; nie rezerwuj dla niego niczego w bieżącym przebiegu, ponieważ zostanie napotkany później. Funkcje i zmienne są pod tym względem traktowane jednakowo.
Jest to bardzo przydatne, jeśli chcesz udostępnić część globalną między modułami i nie chcesz umieszczać / inicjalizować jej w nagłówku.
Z technicznego punktu widzenia każda funkcja w publicznym nagłówku biblioteki jest „extern”, jednak oznaczanie ich jako takich nie przynosi żadnych korzyści, w zależności od kompilatora. Większość kompilatorów może to zrozumieć samodzielnie. Jak widzisz, te funkcje są w rzeczywistości zdefiniowane gdzie indziej.
W powyższym przykładzie main () wypisze hello world tylko raz, ale nadal będzie wprowadzać bar_function (). Zauważ również, że bar_function () nie zwróci w tym przykładzie (ponieważ jest to tylko prosty przykład). Wyobraź sobie, że stop_now jest modyfikowany, gdy sygnał jest obsługiwany (stąd, niestabilny), jeśli nie wydaje się to wystarczająco praktyczne.
Zewnętrzne są bardzo przydatne do takich rzeczy, jak programy obsługi sygnałów, muteks, którego nie chcesz umieszczać w nagłówku lub strukturze itp. Większość kompilatorów optymalizuje, aby upewnić się, że nie rezerwują pamięci dla obiektów zewnętrznych, ponieważ wiedzą o tym Zarezerwuję go w module, w którym zdefiniowany jest obiekt. Jednak ponownie nie ma sensu określanie go przy użyciu nowoczesnych kompilatorów podczas tworzenia prototypów funkcji publicznych.
Mam nadzieję, że to pomoże :)
źródło
bar.c
i deklaracją wfoo.c
. Jeśli funkcja jest zadeklarowana w programiefoo.h
i oba pliki zawierająfoo.h
, nagłówek wymusza spójność między dwoma plikami źródłowymi. Bez tego, jeśli definicjabar_function
wbar.c
zmianach, ale deklaracja wfoo.c
nie zostanie zmieniona, wtedy coś pójdzie nie tak w czasie wykonywania; kompilator nie może wykryć problemu. Kompilator dostrzega problem z prawidłowo użytym nagłówkiem.O ile pamiętam standard, wszystkie deklaracje funkcji są domyślnie traktowane jako „extern”, więc nie ma potrzeby jawnego określania tego.
To nie sprawia, że to słowo kluczowe jest bezużyteczne, ponieważ może być również używane ze zmiennymi (i to w tym przypadku - jest to jedyne rozwiązanie problemów z powiązaniami). Ale z funkcjami - tak, to jest opcjonalne.
źródło
Musisz rozróżnić dwie odrębne koncepcje: definicję funkcji i deklarację symbolu. „extern” to modyfikator linkowania, wskazówka dla kompilatora o tym, gdzie zdefiniowany jest symbol, do którego odwołuje się później (wskazówka brzmi „nie tutaj”).
Jeśli napiszę
w zakresie pliku (poza blokiem funkcyjnym) w pliku C, wtedy mówisz „zmienna może być zdefiniowana gdzie indziej”.
jest zarówno deklaracją funkcji f, jak i definicją funkcji f. Definicja w tym przypadku jest ważniejsza od zewnętrznej.
to najpierw deklaracja, po której następuje definicja.
Użycie opcji
extern
jest niewłaściwe, jeśli chcesz zadeklarować i jednocześnie zdefiniować zmienną o zasięgu pliku. Na przykład,poda błąd lub ostrzeżenie, w zależności od kompilatora.
Użycie opcji
extern
jest przydatne, jeśli jawnie chcesz uniknąć definicji zmiennej.Pozwól mi wyjaśnić:
Powiedzmy, że plik ac zawiera:
Plik ah zawiera:
a plik bc zawiera:
Extern w nagłówku jest przydatny, ponieważ informuje kompilator podczas fazy łączenia, że „to jest deklaracja, a nie definicja”. Jeśli usunę wiersz w ac, który definiuje i, przydzieli dla niego miejsce i przypisze mu wartość, program nie powinien się skompilować z niezdefiniowanym odniesieniem. To informuje dewelopera, że odniósł się do zmiennej, ale jeszcze jej nie zdefiniował. Jeśli z drugiej strony pominę słowo kluczowe „extern” i usunę
int i = 2
wiersz, program nadal się kompiluje - i zostanie zdefiniowany z domyślną wartością 0.Zmienne zakresu pliku są niejawnie definiowane z wartością domyślną 0 lub NULL, jeśli nie przypiszesz do nich jawnie wartości - w przeciwieństwie do zmiennych o zakresie blokowym, które deklarujesz na początku funkcji. Słowo kluczowe extern pozwala uniknąć tej niejawnej definicji, a tym samym pomaga uniknąć błędów.
W przypadku funkcji w deklaracjach funkcji słowo kluczowe jest rzeczywiście zbędne. Deklaracje funkcji nie mają niejawnej definicji.
źródło
int i = 2
wiersza w -3-tym akapicie? I czy słuszne jest stwierdzenie, widzącint i;
, że kompilator przydzieli pamięć dla tej zmiennej, ale widzącextern int i;
, kompilator NIE przydzieli pamięci, ale szuka zmiennej gdzie indziej?Słowo
extern
kluczowe przybiera różne formy w zależności od środowiska. Jeśli deklaracja jest dostępna,extern
słowo kluczowe przyjmuje powiązanie, jak określono wcześniej w jednostce tłumaczeniowej. W przypadku braku takiej deklaracjiextern
określa powiązania zewnętrzne.Oto odpowiednie akapity z projektu C99 (n1256):
źródło
Funkcje wbudowane mają specjalne zasady dotyczące tego, co
extern
oznaczają. (Zauważ, że funkcje wbudowane są rozszerzeniem C99 lub GNU; nie były w oryginalnym C.W przypadku funkcji innych niż wbudowane
extern
nie jest potrzebne, ponieważ jest domyślnie włączone.Zauważ, że zasady dla C ++ są różne. Na przykład
extern "C"
jest potrzebny w deklaracji C ++ funkcji C, które zamierzasz wywołać z C ++, i istnieją różne reguły dotycząceinline
.źródło
Dlatego 10 lat później:
extern
w deklaracji funkcji do usunięcia;git/git
podąża za tym wnioskiem i usuwa goextern
ze swojego kodu (dla Git 2.22, Q2 2019).Zobacz commit ad6dad0 , commit b199d71 , commit 5545442 (29 kwietnia 2019) autorstwa Denton Liu (
Denton-L
) .(Scalenie przez Junio C Hamano -
gitster
- w zatwierdzeniu 4aeeef3 , 13 maja 2019)Nie zawsze jest to jednak proste:
Zobacz commit 7027f50 (04 września 2019) autorstwa Denton Liu (
Denton-L
) .(Scalone przez Dentona Liu -
Denton-L
- w zobowiązaniu 7027f50 , 05 września 2019)Zwróć uwagę, że w Git 2.24 (Q4 2019) wszelkie fałszywe informacje
extern
są odrzucane.Zobacz commit 65904b8 (30 września 2019) autorstwa Emily Shaffer (
nasamuffin
) .Pomógł: Jeff King (
peff
) .Zobacz commit 8464f94 (21 września 2019) autorstwa Dentona Liu (
Denton-L
) .Pomoc: Jeff King (
peff
) .(Scalone przez Junio C Hamano -
gitster
- w zobowiązaniu 59b19bc , 07 października 2019 r.)źródło
Na
extern
informuje słów kluczowych kompilatora, że funkcja lub zmienna ma zewnętrzny podnośnik - innymi słowy, że jest on widoczny z plików innym niż to, w którym jest zdefiniowana. W tym sensie ma znaczenie odwrotne dostatic
słowa kluczowego. Jest to trochę dziwne, aby umieścićextern
definicję w momencie definiowania, ponieważ żadne inne pliki nie miałyby widoczności definicji (lub spowodowałoby to wiele definicji). Zwykle umieszczaszextern
deklarację w pewnym miejscu z widocznością zewnętrzną (na przykład plik nagłówkowy) i umieszczasz definicję w innym miejscu.źródło
zadeklarowanie funkcji extern oznacza, że jej definicja zostanie rozwiązana w momencie linkowania, a nie podczas kompilacji.
W przeciwieństwie do zwykłych funkcji, które nie są zadeklarowane jako zewnętrzne, można je zdefiniować w dowolnym pliku źródłowym (ale nie w wielu plikach źródłowych, w przeciwnym razie pojawi się błąd konsolidatora, który mówi, że podałeś wiele definicji funkcji), w tym jeden w który jest zadeklarowany jako extern, więc w przypadku ur konsolidator rozwiązuje definicję funkcji w tym samym pliku.
Nie sądzę, by było to zbyt użyteczne, jednak robienie tego rodzaju eksperymentów daje lepszy wgląd w to, jak działa kompilator i linker języka.
źródło
Przyczyną braku efektu jest to, że w momencie łączenia konsolidator próbuje rozwiązać definicję extern (w twoim przypadku
extern int f()
). Nie ma znaczenia, czy znajdzie go w tym samym pliku, czy w innym pliku, o ile zostanie znaleziony.Mam nadzieję, że to odpowiada na twoje pytanie.
źródło
extern
w ogóle pozwalać na dodawanie do dowolnej funkcji?