Wpływ słowa kluczowego extern na funkcje C
W C nie zauważyłem żadnego efektu słowa kluczowego extern
użytego przed deklaracją funkcji.
Na początku myślałem, że definiując extern int f();
w pojedynczym pliku wymusza implementowanie go poza zakresem pliku. Jednak okazało się, że zarówno:
extern int f();
int f() {return 0;}
I
extern int f() {return 0;}
Kompilować po prostu dobrze, bez ostrzeżeń z gcc. Użyłem gcc -Wall -ansi
; nie akceptowałby nawet //
komentarzy.
Czy sÄ… jakieÅ› efekty stosowania extern
funkcja przed definicje ? Czy jest to po prostu opcjonalne słowo kluczowe bez skutków ubocznych dla funkcji.
W tym drugim przypadku nie rozumiem, dlaczego standardowi projektanci zdecydowali się zaśmiecić gramatykę zbędnymi słowami kluczowymi.
EDIT: dla wyjaśnienia, wiem, że jest użycie extern
w zmiennych, ale pytam tylko o extern
w funkcje.
9 answers
Mamy dwa akta, foo.c i bar.c.
Tu jest foo.c#include <stdio.h>
volatile unsigned int stop_now = 0;
extern void bar_function(void);
int main(void)
{
while (1) {
bar_function();
stop_now = 1;
}
return 0;
}
Oto bar.c
#include <stdio.h>
extern volatile unsigned int stop_now;
void bar_function(void)
{
if (! stop_now) {
printf("Hello, world!\n");
sleep(30);
}
}
Jak widzisz, nie mamy wspólnego nagłówka między foo.c i bar.c, jednak bar.c potrzebuje czegoś zadeklarowanego w foo.c gdy jest połączony, i foo.c potrzebuje funkcji z paska.c, gdy jest połączony.
używając 'extern', mówisz kompilatorowi, że to, co nastąpi po nim, zostanie znalezione (niestatyczne) w czasie linku; nie rezerwuj niczego dla niego w bieżącym przejściu, ponieważ zostanie on 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ć go w nagłówku.
Technicznie, każda funkcja w nagłówku publicznym biblioteki jest 'extern', jednak etykietowanie ich jako takich ma bardzo niewiele lub nie ma żadnej korzyści, w zależności od kompilatora. Większość kompilatorów może to rozgryźć na własną rękę. Jak widzisz, funkcje te są rzeczywiście zdefiniowane gdzie indziej.
W powyższym przykładzie main() wyświetli hello world tylko raz, ale nadal wprowadzi funkcję bar_function (). Zauważ również, że funkcja bar_function() nie powró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 Lotny), jeśli nie wydaje się to wystarczająco praktyczne.
Externs są bardzo przydatne do takich rzeczy jak obsługa sygnałów, mutex, którego nie chcesz umieścić w nagłówku lub strukturze itp. Większość kompilatorów zoptymalizuje, aby upewnić się, że nie rezerwują żadnej pamięci dla zewnętrznych obiektów, ponieważ wiedzą, że będą ją rezerwować w module, w którym zdefiniowany jest obiekt. Jednak, ponownie, nie ma sensu określać go za pomocą nowoczesnych kompilatorów podczas prototypowania funkcji publicznych.
Mam nadzieję, że to pomoże:)
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
2020-12-09 13:34:13
O ile pamiętam standard, wszystkie deklaracje funkcji są domyślnie traktowane jako "extern", więc nie ma potrzeby ich jawnie określać.
To nie czyni tego słowa kluczowego bezużytecznym, ponieważ może być również używane ze zmiennymi(i w tym przypadku - jest to jedyne rozwiązanie do rozwiązywania problemów z połączeniem). Ale z funkcjami-tak, jest to opcjonalne.
Należy rozróżnić dwa odrębne pojęcia: definicję funkcji i deklarację symbolu. "extern" jest modyfikatorem linkowania, podpowiedzią do kompilatora o tym, gdzie jest zdefiniowany symbol, o którym mowa później (podpowiedź brzmi: "nie tutaj").
Jeśli napiszę
extern int i;
W obszarze pliku (poza blokiem funkcji) w pliku C, to mówisz "zmienna może być zdefiniowana gdzie indziej".
extern int f() {return 0;}
Jest zarówno deklaracją funkcji f, jak i definicją funkcji f. definicja w tym przypadku przejeżdża na zewnątrz.
extern int f();
int f() {return 0;}
Jest najpierw deklaracją, a następnie definicją.
Użycie extern
jest złe, jeśli chcesz zadeklarować i jednocześnie zdefiniować zmienną zakresu pliku. Na przykład,
extern int i = 4;
Wyświetli błąd lub ostrzeżenie, w zależności od kompilatora.
Użycie extern
jest przydatne, jeśli wyraźnie chcesz uniknąć definicji zmiennej.
Pozwól mi wyjaśnić:
Powiedzmy, że plik a. c zawiera:
#include "a.h"
int i = 2;
int f() { i++; return i;}
Plik a. h zawiera:
extern int i;
int f(void);
A plik b. C zawiera:
#include <stdio.h>
#include "a.h"
int main(void){
printf("%d\n", f());
return 0;
}
Extern w nagłówku jest użyteczny, ponieważ mówi kompilatorowi podczas fazy łącza, "to jest deklaracja, a nie definicja". JeĹ "li usunÄ ™ liniÄ ™ w a.c, ktĂłra definiuje i, przydziaĺ' a dla niej spacje i przypisze jej wartoĹ " ć, program nie powinien kompilowaä ‡ siÄ ™ z niezdefiniowanym referencjÄ.... To mówi deweloperowi, że odwołał się do zmiennej, ale jeszcze nie zdefiniował to. Jeśli z drugiej strony pominę słowo kluczowe "extern" i usunę linię int i = 2
, program nadal będzie kompilował - i zostanie zdefiniowany z domyślną wartością 0.
Zmienne zakresu pliku są domyślnie definiowane wartością domyślną 0 lub NULL, jeśli nie przypisujesz im jawnie wartości - w przeciwieństwie do zmiennych zakresu bloków, które deklarujesz u góry funkcji. Słowo kluczowe extern unika tej ukrytej definicji, a tym samym pomaga uniknąć błędów.
Dla funkcji, w funkcji deklaracje, słowo kluczowe jest rzeczywiście zbędne. Deklaracje funkcji nie mają definicji niejawnej.
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-09-05 18:56:59
SÅ‚owo kluczowe extern
przybiera różne formy w zależności od środowiska. Jeśli deklaracja jest dostępna, słowo kluczowe extern
przyjmuje powiązanie tak, jak zostało to określone wcześniej w jednostce tłumaczenia. W przypadku braku takiej deklaracji, extern
określa powiązania zewnętrzne.
static int g();
extern int g(); /* g has internal linkage */
extern int j(); /* j has tentative external linkage */
extern int h();
static int h(); /* error */
Oto odpowiednie paragrafy z projektu C99 (n1256):
6.2.2 powiązania identyfikatorów
[...]
4 dla identyfikatora zadeklarowanego z storage-class specifier extern in a scope, in which a prior declaration of that identifier is visible, 23) if the prior declaration specificates internal or powiązania zewnętrzne, powiązanie identyfikatora w późniejszej deklaracji jest takie samo jak powiązania określone we wcześniejszym zgłoszeniu. Jeżeli nie jest widoczna Żadna wcześniejsza deklaracja, lub jeżeli wcześniejsza deklaracja nie określa powiązania, wtedy identyfikator ma powiązania zewnętrzne.
5 jeżeli zgłoszenie identyfikatora dla funkcja nie ma specyfika klasy storage, jej powiązanie jest określana dokładnie tak, jakby została zadeklarowana za pomocą specyfikacji storage-class extern. Jeśli deklaracja identyfikatora obiektu ma zakres pliku i nie ma określenia klasy storage, jego połączenie jest zewnętrzne.
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
2009-05-13 19:52:13
Funkcje Inline mają specjalne reguły dotyczące tego, co oznacza extern
. (Zauważ, że funkcje inline są rozszerzeniem C99 lub GNU; nie były w oryginalnym C.
Dla funkcji nie-inline, extern
nie jest potrzebna, ponieważ jest domyślnie włączona.
Zauważ, że zasady C++ są różne. Na przykład, extern "C"
jest potrzebne w deklaracji funkcji C++, które będziesz wywoływać z C++, a istnieją różne zasady dotyczące inline
.
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
2009-05-13 16:44:19
IOW, extern jest zbędny i nic nie robi.
Dlatego, 10 lat później:
- narzędzie transformacji kodu, takie jak Coccinelle będzie miało tendencję do znacznika
extern
w deklaracji funkcji do usunięcia; - a codebase like
git/git
wynika z tego wniosku i usuwaextern
ze swojego kodu(dla Git 2.22, Q2 2019).
Zobacz Zatwierdź ad6dad0, / align = "left" / 199d71, commit 5545442 (29 Kwiecień 2019) by Denton Liu (Denton-L
).
(dodany przez Junio C Hamano -- gitster
-- w commit 4aeeef3, 13 maja 2019)
*.[ch]
: Usuńextern
z deklaracji funkcji za pomocÄ…spatch
Nastąpiło naciśnięcie, aby usunąć
extern
z deklaracji funkcji.Usuń niektóre instancje "
extern
" dla deklaracji funkcji, które są przechwytywane przez Coccinelle.
Zauważ, że Coccinelle ma pewne trudności z przetwarzanie funkcji z__attribute__
lub varargami, więc niektóre deklaracjeextern
zostaną pozostawione do rozpatrzenia w przyszłej łatce.Użyto plastra Coccinelle:
@@ type T; identifier f; @@ - extern T f(...);
I było:
$ git ls-files \*.{c,h} | grep -v ^compat/ | xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place
Nie zawsze jest to jednak proste:]} Jeśli chcesz dowiedzieć się więcej o naszych produktach, skontaktuj się z nami.(
Denton-L
).(dodane przez Denton Liu --
Denton-L
-- in commit 7027f50, 05 Sep 2019)
compat/*.[ch]
: Usuń {[5] } z deklaracji funkcji za pomocą spatchIn 5545442 (
*.[ch]
: removeextern
from function declarations using spatch, 2019-04-29, Git v2.22.0-RC0), usunęliśmy zewnętrzne deklaracje funkcji za pomocąspatch
, ale celowo wykluczyliśmy pliki wcompat/
, ponieważ niektóre z nich są bezpośrednio kopiowane z zewnętrznego źródła i powinniśmy unikać ich ubijania, aby ręczne łączenie przyszłych aktualizacji było prostsze.W ostatnim commit, ustaliliśmy pliki, które pobrały z upstream więc możemy je wykluczyć i uruchomić
spatch
na pozostałej części.Użyto plastra Coccinelle:
@@ type T; identifier f; @@ - extern T f(...);
I było:
$ git ls-files compat/\*\*.{c,h} | xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place $ git checkout -- \ compat/regex/ \ compat/inet_ntop.c \ compat/inet_pton.c \ compat/nedmalloc/ \ compat/obstack.{c,h} \ compat/poll/
Coccinelle ma problemy z
__attribute__
} i varargs więc wykonaliśmy następujące czynności, aby upewnić się, że żadne pozostałe zmiany nie zostały pozostawione za:$ git ls-files compat/\*\*.{c,h} | xargs sed -i'' -e 's/^\(\s*\)extern \([^(]*([^*]\)/\1\2/' $ git checkout -- \ compat/regex/ \ compat/inet_ntop.c \ compat/inet_pton.c \ compat/nedmalloc/ \ compat/obstack.{c,h} \ compat/poll/
Zauważ, że w Git 2.24 (Q4 2019) wszelkie fałszywe extern
sÄ… upuszczony.
nasamuffin
).Helped-by: Jeff King (
peff
).Zobacz commit 8464f94 (21 Sep 2019) przez Denton Liu (
Denton-L
).Helped-by: Jeff King (
peff
).(dodany przez Junio C Hamano --
gitster
-- in commit 59b19bc, 07 Oct 2019)
promisor-remote.h
: dropextern
from function declarationPodczas tworzenie tego pliku, za każdym razem gdy wprowadzana była nowa deklaracja funkcji, zawierało
extern
.
Jednak poczÄ…wszy od 5545442 (*.[ch]
: removeextern
from function declarations usingspatch
, 2019-04-29, Git v2.22.0-RC0), we 've been activities to preview to externs from being used in function declarations because they' re needless.Usuń te fałszywe
extern
s.
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
2019-10-12 20:20:35
SÅ‚owo kluczowe extern
informuje kompilator, że funkcja lub zmienna ma zewnętrzne powiązanie - innymi słowy, że jest widoczna z plików innych niż ten, w którym jest zdefiniowana. W tym sensie ma ono znaczenie odwrotne do słowa kluczowego static
. Trochę dziwnie jest umieścić extern
w momencie definicji, ponieważ żadne inne pliki nie będą miały widoczności definicji (lub spowodowałoby to powstanie wielu definicji). Zwykle umieszczasz extern
w deklaracji w pewnym momencie Z zewnętrznym widoczność (np. plik nagłówkowy) i umieścić definicję w innym miejscu.
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
2009-05-13 08:02:41
Deklarowanie funkcji extern oznacza, że jej definicja zostanie rozwiązana w czasie łączenia, a nie podczas kompilacji.
W przeciwieństwie do zwykłych funkcji, które nie są zadeklarowane extern, może być zdefiniowana w dowolnym pliku źródłowym(ale nie w wielu plikach źródłowych, w przeciwnym razie pojawi się błąd linkera mówiący, że podałeś wiele definicji funkcji), w tym tę, w której jest zadeklarowana extern.So, w przypadku ur linker rozwiązuje definicję funkcji w tym samym plik.
Nie sÄ ... dzÄ™, Ĺźe robienie tego byĹ 'oby bardzo przydatne, jednak robienie tego typu eksperymentăłw daje lepszy wglÄ ... d w jak dziaĹ' a kompilator i linker jÄ ™ zyka.
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-07-15 06:40:47
Powodem, dla którego nie ma żadnego efektu jest to, że w czasie linkowania linker 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.
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
2012-12-20 01:58:43