Forum: PC-Programmierung überflüssiges extern?


von markus (Gast)


Lesenswert?

Hallo,
ich habe einen SourceCode (in c) bekommen, den ich mir angucken soll und 
dann weiter bearbeiten soll.

Was mir auffällt, das alle Funktionen und Variablen, die in anderen 
dateien benutzt werden als extern deklariert sind. Die Header wird aber 
in den jeweiligen c-files eingebunden.

file1.h
1
extern uint8 x;
2
extern void function(void);
file1.c
1
void function(void)
2
{
3
...
4
}

file2.c
1
#include file1.h
2
...
3
...
4
function();
5
...

Aber ich inkludiere doch file1.h. Damit sind die Funktionen ja 
eigentlich vorhanden? Warum sind die Funktionen als extern deklariert?

von Dr. Sommer (Gast)


Lesenswert?

markus schrieb:
> Warum sind die Funktionen als extern deklariert?

Weil der Programmierer nicht wusste, dass "extern" bei Funktionen 
komplett wirkungslos ist. Das kann man genau so gut weg lassen.

von Ralph S. (jjflash)


Lesenswert?

In file2.c verwendest du function(), die in file2.c nicht definiert ist. 
Wäre function() nicht als extern deklariert. würde der Compiler den Code 
für function() in der Datei file2.c suchen und dort nicht finden, weil 
nicht vorhanden. file2.c wäre somit nicht compilierbar.

Die Deklaration als extern fügt hier, flapsig ausgedrückt, ein Symbol 
ein, das später beim Zusammensetzen des gesamten Programms der Linker 
referenzieren muss.

Somit kannst du bspw. eine Bibliothek für Displays schreiben, die für 
die Ausgaben ein putpixel benötigen, die Funktion putpixel jedoch wird 
in einer Datei definiert die den Treiber für das Display darstellt.

Somit können dann alle Funktionen für das Display wie zeichnen von 
Linien, Rechtecken, Kreisen etc. in einer Datei stehen unabhängig davon, 
welches Display verwendet werden soll. In deiner Treiberdatei steht dann 
das putpixel. Arbeitest du mit mehreren unterschiedlichen Displays musst 
du dann beim build nur den Treiber zu deinem Programm hinzulinken und 
das putpixel wird von diesem verwendet (dein Programmgerüst oben zeigt 
das ja schon sehr deutlich).

Zusammengefasst:

Die Deklaration als extern sorgt dafür, dass bei dir file2.c übersetzbar 
ist, obwohl der Code für function() nicht bekannt ist.

von Dr. Sommer (Gast)


Lesenswert?

Ralph S. schrieb:
> In file2.c verwendest du function(), die in file2.c nicht
> definiert ist.
> Wäre function() nicht als extern deklariert. würde der Compiler den Code
> für function() in der Datei file2.c suchen und dort nicht finden, weil
> nicht vorhanden. file2.c wäre somit nicht compilierbar.

Falsch. Ohne "extern" macht er das genau gleich. Es gibt absolut 
keinen Unterschied zwischen:
1
extern void function(void);
und
1
void function(void);

Er würde die Funktion nur dann nicht finden, wenn man diese Deklaration 
ganz weglassen würde.

Bei Variablen ist das hingegen nicht so, da gibt es einen Unterschied.
1
extern uint8_t x;
Besagt, dass später (oder in einer anderen Datei) die Definition 
erfolgt;
1
uint8_t x;
ist eine solche Definition.

von Hugo (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Das kann man genau so gut weg lassen.

Wenn du mal irgendwann Langeweile haben solltest, kannst du gerne mal 
ein C-Buch lesen ;-)

Das bringt was. Ehrlich!

von Dr. Sommer (Gast)


Lesenswert?

Hugo schrieb:
> Das bringt was. Ehrlich!

Das Echauffieren über die Fehler und unverständlichen Erklärungen ist 
schlecht für meinen Blutdruck.

https://jameshfisher.com/2017/08/28/c-extern-function/

"Applied to a function declaration, the extern keyword in fact does 
nothing: the declaration extern int incr(int) is exactly the same as int 
incr(int). This is because all function declarations have an implicit 
extern applied!"

von Joachim B. (jar)


Lesenswert?

Hugo schrieb:
> Wenn du mal irgendwann Langeweile haben solltest, kannst du gerne mal
> ein C-Buch lesen ;-)

weil alles Geschriebene in Bücher immer wahr ist?

ne das ist nur ein Irrglaube

ich schliesse mich Dr. Sommer an, bei Funktionen ist aktuell extern 
unnötig.

: Bearbeitet durch User
von markus (Gast)


Lesenswert?

OK,
vielleicht haben die das ja auch nur hingeschrieben, um es sofort 
ersichtlich zu machen, dass diese Funktion noch in anderen files benutzt 
wird? Aber andersrum, würde 'ich' Funktionen, die nur in einer file 
benötigt werden in der c-file direkt als static deklarieren.
Aber wenn es sonst keinen unterschied macht, kann ich damit leben. 
Wenigstens ist es einheitlich.

Danke euch schonmal für die Erläuterungen

von Dr. Sommer (Gast)


Lesenswert?

markus schrieb:
> vielleicht haben die das ja auch nur hingeschrieben, um es sofort
> ersichtlich zu machen, dass diese Funktion noch in anderen files benutzt
> wird?

Das ist bei Funktions-Deklarationen ohne "extern" in Header-Files 
ebenfalls ersichtlich.

Die übliche Regel ist:
- Funktionen, die nur in 1 .c-Datei gebraucht werden, werden in dieser 
.c-Datei mit "static" definiert, und ggf. am Anfang dieser Datei nochmal 
deklariert
- Funktionen, die in mehr Dateien gebraucht werden, werden in der .c 
Datei ohne "static" definiert und in der zugehörigen .h Datei 
deklariert.
- Die .h Datei wird von der zugehörigen .c Datei inkludiert, um den 
Compiler Unstimmigkeiten finden zu lassen.
- extern wird bei Funktionen nie benutzt

- Variablen, die nur in 1 .c-Datei gebraucht werden, werden am Anfang 
dieser .c-Datei mit "static" definiert
- Variablen, die in mehr Dateien gebraucht werden, werden in der .c 
Datei ohne "static" definiert und in der zugehörigen .h Datei mit 
"extern" deklariert (hier die einzige Nutzung von extern!).
- Die .h Datei wird von der zugehörigen .c Datei inkludiert.

- Alle von Funktions- und Variablendeklarationen benötigten Typen 
(typedef,struct,union) werden entweder davor in der .h Datei definiert, 
oder in der .h Datei per #include eingebunden
- Alle .h Dateien haben Include-Guards (#ifndef-#define-#endif)

von Rolf M. (rmagnus)


Lesenswert?

Dr. Sommer schrieb:
> markus schrieb:
>> vielleicht haben die das ja auch nur hingeschrieben, um es sofort
>> ersichtlich zu machen, dass diese Funktion noch in anderen files benutzt
>> wird?
>
> Das ist bei Funktions-Deklarationen ohne "extern" in Header-Files
> ebenfalls ersichtlich.

Vor allem, da es um eine Deklaration im Header geht. Die macht man ja 
gerade, damit die Funktion in anderen Files benutzt werden kann.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

markus schrieb:
> ich habe einen SourceCode (in c) bekommen, den ich mir angucken soll
> und dann weiter bearbeiten soll.

> extern void function(void);

Auch wenn das extern hier redundant ist, ich würd das einfach so lassen. 
Das extern rauszukicken macht den Code weder effizienter noch besser 
nachvollziehbar oder besser zu supporten.

Übrigens bedeutet das extern nicht notwendigerweise, dass function 
external Linkage hat, insbesondere wenn eine static Deklaration / 
Definition dem extern vorausgeht:
1
static int f (void);
2
extern int f (void);

ist gültiges C/C++.  Aber das nur nebenbei, das ist eine "Feature" von 
C/C++, und daran ändert auch ein Code-Review nix.

Für ein Review würde ich starten mit statischer Codeanalyse, d.h. 
Warnungen aktivieren, auch solche die standardmäßig nicht an sind und 
das Ergebnis bewerten.  Und statische Analyse-Tools wie Lint verwenden.

Was Prototypen betrifft, z.B. -Wstrict-protoypes -Wmissing-prototypes 
aktivieren falls GCC zum Einsatz kommt.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Es ist auch denkbar, dass die Deklaration

extern void function(void);

zunächst im Kopf von file1.c stand, dann aber festgestellt wurde, dass 
Andere Module function() ebenfalls nutzen können sollten, und 
konsequenterweise die Deklaration einfach mit Cut-und-Paste in einen 
Headerfile ausgelagert wurde.

Die extern Deklaration im Quellfile kann als Explizitmachen, dass die 
Funktion anderswo residiert, Sinn machen (sozusagen ein Gegenpol zu 
static, obwohl natürlich static für den Compiler einen Unterschied 
macht, extern aber nicht). Man kann auch argumentieren, dass es genau 
dann Sinn macht, wenn man eben vermeiden will, dass Anderen Module die 
Funktion pauschal sehen können (manchmal sind logisch zusammen gehörende 
Funktionsgruppen über mehrere Quelldateien verteilt, um die Quellen 
nicht unnötig aufzublähen, aber aus Modularisierungsgründen sollten die 
zusammengehörenden Funktionsgruppen nicht global sichtbar sein).

Klar kann man dafür auch lokale Headerfiles verwenden. Oder noch Andere 
Konstrukte. Oder die Entwickler feuern, die beim Cut-und-Paste vom 
Quell- in den Headerfile nicht daran denken, das extern zu löschen. Oder 
C ganz ablehnen, weil es so vieles durchgehen lässt. Oder oder oder. 
Aber das ist müssig, weil vieles davon Stil- oder Konventionsfragen 
sind, bei denen jede Variante begründet werden kann (so wie Kernighan 
Ritchie oder Allman Klammerung. Manche mögen Kartoffeln lieber, manche 
Nudeln).

von Uhu U. (uhu)


Lesenswert?

Dr. Sommer schrieb:
> - Variablen, die nur in 1 .c-Datei gebraucht werden, werden am Anfang
> dieser .c-Datei mit "static" definiert

Das ist eine etwas unglückliche Formulierung. static verhindert, dass 
Variablen in anderen Modulen sichtbar werden können, sie werden also vor 
Zugriffen von außen geschützt.

Nich-static-Variablen können in beliebieg vielen Modulen benutzt werden, 
also auch in nur einem.

von Rolf M. (rmagnus)


Lesenswert?

Johann L. schrieb:
> markus schrieb:
>> ich habe einen SourceCode (in c) bekommen, den ich mir angucken soll
>> und dann weiter bearbeiten soll.
>
>> extern void function(void);
>
> Auch wenn das extern hier redundant ist, ich würd das einfach so lassen.
> Das extern rauszukicken macht den Code weder effizienter noch besser
> nachvollziehbar oder besser zu supporten.

Es führt aber - wie man ja hier hervorragend sehen kann - zu 
Irritationen, weil es sehr ungebräuchlich ist.

von W.S. (Gast)


Lesenswert?

markus schrieb:
> Aber ich inkludiere doch file1.h. Damit sind die Funktionen ja
> eigentlich vorhanden? Warum sind die Funktionen als extern deklariert?

Eigentlich ist es alles ganz einfach:

Um eine Funktion korrekt aufrufen zu können, braucht man deren 
Deklaration, wo der Compiler sehen kann, was diese Funktion an 
Argumenten haben will und was sie denn so zurückliefert.

Ob nun die Funktion, die man aufrufen will, schlichtweg weiter oben im 
Quelltext steht, oder ob mal bloß deren Kopfzeile als Prototyp weiter 
oben im Quelltext steht, ist für's Aufrufen egal.

Bei Daten sieht das anders aus, da gibt es keine Prototypen. Also MUSS 
man bei Daten, die eben nicht weiter oben im aktuellen Quelltext stehen, 
ein 'extern' davorschreiben, damit der Compiler den Typ erkennen kann, 
obwohl die eigentlichen Daten irgendwo in irgend einer anderen Datei 
stehen - das kann auch die aktuelle Datei sein.

Nun kann man in einem Headerfile sich die Mühe machen, säuberlich zu 
unterscheiden, ob irgendwas nun Funktion oder Daten ist, oder man 
schreibt ganz einfach vor beides 'extern' davor - auch in Anbetracht 
dessen, daß dieses bei Funktionen nicht unbedingt notwendig ist.

Aber es ist eine sinnvolle Vereinheitlichung, mit der man wenigstens 
einmal eine Extrawurst in C beseitigt hat. Und es trägt zur Lesbarkeit 
bei, weil menschliche Betrachter sofort sehen können, daß das als extern 
bezeichnete Dingsbums eben nicht die Sache als solche ist, sondern eine 
Referenz für etwas, das woanders steht.

W.S.

von Dr. Sommer (Gast)


Lesenswert?

W.S. schrieb:
> Und es trägt zur Lesbarkeit bei, weil menschliche Betrachter sofort
> sehen können, daß das als extern bezeichnete Dingsbums eben nicht die
> Sache als solche ist, sondern eine Referenz für etwas, das woanders
> steht.

Das sieht man auch daran, dass auf das ) kein { sondern ein ; folgt. Ist 
dann auch konsistent mit vielen anderen Sprachen, in denen es das extern 
gar nicht gibt.

von Rolf M. (rmagnus)


Lesenswert?

Wenn du das so konsequent verfolgst, müsstest du statt long auch signed 
long int schreiben, denn long ist eigentlich nur ein Zusatz für den 
darauf folgenden Typnamen. Wenn man den weglässt, wird implizit int 
angenommen.  Und signed ist auch eigentlich nicht nötig, wäre aber dann 
konsequent, um es von unsigned zu unterscheiden.
Und wie ist es bei lokalen nicht-statischen Variablen? Da weiß ja 
keiner, was das ist, wenn man nicht explizit auto davorschreibt, zur 
Unterscheidung von static. Ein sehr verkanntes Schlüsselwort.

von Timm R. (Firma: privatfrickler.de) (treinisch)


Lesenswert?

Dr. Sommer schrieb:
> W.S. schrieb:
>> Und es trägt zur Lesbarkeit bei, weil menschliche Betrachter sofort
>> sehen können, daß das als extern bezeichnete Dingsbums eben nicht die
>> Sache als solche ist, sondern eine Referenz für etwas, das woanders
>> steht.
>
> Das sieht man auch daran, dass auf das ) kein { sondern ein ; folgt. Ist
> dann auch konsistent mit vielen anderen Sprachen, in denen es das extern
> gar nicht gibt.

für mich geht der Punkt an Dich Dr. Sommer, bzw an die 
nicht-extern-hinschreiben Fraktion.

Solche Dinge liest man nicht denkend, sondern halb bewusst, halb 
automatisch. Genau wie man viele Wörter nicht Buchstabe für Buchstabe 
oder Silbe für Silbe zusammenstückelt, sondern am Stück als Muster 
verdaut.

Die idiomatische Schreibweise ist es ganz klar, das "extern" bei 
Funktionen nicht hinzuschreiben. Weicht man von der idiomatischen 
Formulierung ab, staunt der Laie und der Fachmann wundert sich. Das 
sollte man nicht ohne Not tun.

vlg
 Timm

von W.S. (Gast)


Lesenswert?

Rolf M. schrieb:
> Wenn du das so konsequent verfolgst, müsstest du..

Ich? nein. Sondern 'man', sprich die C-Gremien. Was du schreibst, ist ja 
in allen Punkten etwas, wo es in C im Argen liegt. Keine sauberen Typen, 
kein Modulkonzept, überall Nebeneffekte, überall Defaults und 
Extrawürste (sprich Workarounds)...


Weißt du, die Sprache C ist ganz genau so wie das Haus der Weasley's bei 
Harry Potter:

Ursprünglich ein verlassener alter Schweinestall, dann hier was angebaut 
und dort was aufgestockt und da nochmal was drangebaut - aber im Kern 
noch immer der alte Schweinestall.

Das wirft auch ein Licht auf die Gilde der Programmierer, die sich darin 
offensichtlich am allerwohlsten fühlen.

Naja, das Essen mit Messer und Gabel liegt ja auch nicht jedem, aus dem 
Trog schmeckt's manchem wohl besser und ist auch nicht so kompliziert, 
gelle - siehe McDonalds.

W.S.

von Rolf M. (rmagnus)


Lesenswert?

Ich habe Programmiersprachen (und die Menschen, die sie verwenden) 
früher auch ähnlich emotional gesehen, wobei mich meist eher gestört 
hat, wenn in Sprachen Features, die eigentlich nützlich sind, 
weggelassen werden, weil man den Programmierer für zu blöd hält, um sie 
richtig zu verwenden. Das war für mich dann auch eine Aussage über die 
Programmierer, die diese Sprache gerne verwenden - so ähnlich wie du es 
gerade beschreibst.
Inzwischen versuche ich, die Emotionen rauszulassen und die Sprachen 
einfach nur als Werkzeuge zu sehen, die ihre Stärken und Schwächen 
haben. Und ich nutze einfach das, was für die Aufgabe geeignet ist und 
womit ich am besten zurecht komme.

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

Rolf M. schrieb:
> Inzwischen versuche ich, die Emotionen rauszulassen

Ja. Das ist ja auch vollkommen richtig so. Ich mache das ganz genau so. 
Aber es kommt hier ja garnicht drauf an, dir oder mir die Sache mit dem 
'extern' zu erklären, sondern ihm:

markus schrieb:
> Was mir auffällt, das alle Funktionen und Variablen, die in anderen
> dateien benutzt werden als extern deklariert sind. Die Header wird aber
> in den jeweiligen c-files eingebunden.

Hier haben wir also jemanden, der noch logisch denken kann und das auch 
tut - und dem fällt sowas natürlich auf, denn man nimmt ja zuvörderst 
an, daß gerade hinter einer Programmiersprache Logik steckt.

Aber das ist hier eben nicht so, siehe Weasley's Haus. Die Logik fehlt 
und die diversen "Ist hier so" gibt es nur deshalb, weil sie als 
Workaround gegen andere Mißstände notwendig sind.

Muß man also im Jahre 2019 noch auswendig lernen, daß bei externen 
Funktionen kein 'extern' davor muß, wo doch bei externen Daten 
selbiges notwendig ist?

Nö, muß man nicht.

W.S.

von A. S. (Gast)


Lesenswert?

W.S. schrieb:
> Muß man also im Jahre 2019 noch auswendig lernen, daß bei externen
> Funktionen kein 'extern' davor muß, wo doch bei externen Daten selbiges
> notwendig ist?

Ja, kann man so sehen.

Geht aber auch pragmatischer: da die Funktionsdeklaration auch ohne 
extern eindeutig ist, kann dies weggelassen werden. Das hat sich bei 
vielem anderen auch bewährt, z.b. init mit 0, auto, long int, ... .

Ja, man kann Pendant sein. Aber im Long Run gewinnt hier das 
pragmatische bei Aufwand/Nutzen

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

W.S. schrieb:
> Muß man also im Jahre 2019 noch auswendig lernen, daß bei externen
> Funktionen kein 'extern' davor muß, wo doch bei externen Daten selbiges
> notwendig ist?

Da es bei Funktionen das Konzept der Vorwärtsdeklaration (durch den 
Funktionsprototypen) gibt, wird man diese kognitive Leistung wohl 
aufbringen müssen. Wie man im Leben überhaupt oft irgendwelche Dinge 
lernen muss.

Immerhin:
Ein überflüssiges "extern" richtet keinen Schaden an, außer zu 
vermitteln, daß derjenige, der es verwendet hat, nicht so recht 
verstanden hat, was es bedeutet.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rufus Τ. F. schrieb:
> Ein überflüssiges "extern" richtet keinen Schaden an, außer zu
> vermitteln, daß derjenige, der es verwendet hat, nicht so recht
> verstanden hat, was es bedeutet.

Ok, ab sofort darfst du keine avr-libc mehr verwenden :-)

von Carl D. (jcw2)


Lesenswert?

Johann L. schrieb:
> Rufus Τ. F. schrieb:
>> Ein überflüssiges "extern" richtet keinen Schaden an, außer zu
>> vermitteln, daß derjenige, der es verwendet hat, nicht so recht
>> verstanden hat, was es bedeutet.
>
> Ok, ab sofort darfst du keine avr-libc mehr verwenden :-)

Es ist schon lustig, wie aus
"es muß nicht dastehen, weil es sich aus dem Kontext eh eindeutig 
ergibt"
zu
"es darf nicht verwendet werden"
wird.
Aber wenn das die wichtigsten Probleme sind, dann geht es uns ja 
wenigstens gut.  ;-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Carl D. schrieb:
> Johann L. schrieb:
>> Rufus Τ. F. schrieb:
>>> Ein überflüssiges "extern" richtet keinen Schaden an, außer zu
>>> vermitteln, daß derjenige, der es verwendet hat, nicht so recht
>>> verstanden hat, was es bedeutet.
>>
>> Ok, ab sofort darfst du keine avr-libc mehr verwenden :-)
>
> Es ist schon lustig, wie aus "es muß nicht dastehen, weil es sich
> aus dem Kontext eh eindeutig ergibt" zu "es darf nicht verwendet
> werden" wird.

Na wenn Carl meint, dass die avr-libc Autoren planlos waren und keine 
Ahnung von dem, was sie da fabrizierten, wird er diese Software kaum 
verwenden wollen...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Na wenn Carl meint, dass die avr-libc Autoren planlos waren und keine
> Ahnung von dem, was sie da fabrizierten,

Ob Carl das meint?

Ich jedenfalls stehe dazu, daß ich "extern" bei Funktionsdeklarationen 
für überflüssig halte und daß ich der Ansicht bin, daß Leute, die es 
verwenden, nicht so recht verstanden haben, was es bedeutet.

Wenn die avr-libc so geschrieben ist, sei es drum. Es macht sie nicht 
unbrauchbar, es ... verwundert nur etwas.

(Es gibt auch durchaus professionell verwendbare Software, die von 
Leuten mit merkwürdigen Ansichten und Designentscheidungen geschrieben 
wurde)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rufus Τ. F. schrieb:
> Ob Carl das meint?

ups, mein Fehler.

von Yalu X. (yalu) (Moderator)


Lesenswert?

In C ist jede Funktion extern in dem Sinn, dass sie außerhalb jeder
anderen Funktion definiert wird¹. Das Schlüsselwort extern bezieht
sich auf genau diese Eigenschaft und nicht – wie oft irrtümlicherweise
angenommen – darauf, dass die deklarierte Funktion in einer anderen
Übersetzungseinheit definiert ist.

Betrachten wir die beiden folgenden Deklarationen:

1
void f(void);

und

1
extern void f(void);

Mit der ersten drückt man (vereinfacht) aus:

  "Ich möchte die Funktion f benutzen."

Mit der zweiten hingegen:

  "Ich möchte von allen Funktionen namens f diejenige benutzen, die
  außerhalb aller anderen Funktionen definiert wurde."

Da die Zusatzbedingung im zweiten Fall trivialerweise immer erfüllt ist,
ist sie – und damit auch das extern – überflüssig.

Da es (fast immer) egal ist, ob man das extern vor Funktionsprototypen
benutzt oder nicht, besteht die Möglichkeit, es zu Dokumentationszwecken
zu nutzen:

- Funktionen, die in mehreren Übersetzungseinheiten genutzt werden (also
  insbesondere auch Bibliothekfunktionen), werden als extern
  deklariert (auch wenn dies nicht der eigentlichen Bedeutung des
  Schlüsselworts entspricht.

- Bei Funktionen, die nur innerhalb einer einzigen Übersetzungseinheit
  genutzt werden, wird das extern weggelassen.

Diese Vorgehensweise scheint in mehreren prominenten Quellen wie bspw.

- der ISO-Norm,
- dem GCC und
- der GNU C Library (glibc)

zur Anwendung zu kommen.

Ich persönlich halte davon dennoch nicht viel, da man die Unterscheidung
der beiden o.g. Fälle ebenso gut durch die Verwendung bzw. das Weglassen
von static erreicht. Da die Verwendung von static für dateilokale
Funktionen aus Kapselungs- und Optimierungsgründen ohnehin ratsam ist,
erschlägt man damit gleich zwei FLiegen mit einer Klappe.

M.W. gibt es in C nur einen einzigen Fall, wo extern in einer
Funktionsdelaration einen semantischen Unterschied macht, nämlich in
Verbindung mit einer inline-Deklaration:

Mit

1
inline void f(void) {
2
  // ...
3
}

wird eine so genannte "inline definition" der Funktion f angelegt. Damit
kann die Funktion zwar geinlinet, aber weder per Call aufgerufen noch
per Funktionszeiger referenziert werden.

Fügt man hingegen ein extern hinzu, erzeugt der Compiler eine
"external definition", so dass f auch wie jede Nicht-Inline-Funktion
genutzt werden kann:

1
extern inline void f(void) {
2
  // ...
3
}

Allerdings wird man diese Methode, eine external Definition zu erzeugen,
aus den folgenden Gründen in der Praxis kaum nutzen:

- Wird f nur in einer einzigen Übersetzungseinheit genutzt, wird man es
  als static inline deklarieren. Dadurch wird ebenfalls eine external
  Definition erzeugt, wobei der erzeugte Programmcode ggf. vom Compiler
  wieder wegoptimiert wird.

- Wird f in mehreren Übersetzungseinheiten genutzt, schreibt man die
  Inline-Definition von f in ein .h-File. Man kann f dort wie oben als
  static inline deklarieren, oder man deklariert es nur als inline
  und schreibt für die Erzeugung der external Definition  eine weitere
  Deklaration in eines der .c-Files:

1
  extern void f(void);

  Hier kann man wiederum das extern weglassen und einfach

1
  void f(void);

  schreiben.

Aus den genannten Gründen verwende ich in Funktionsdeklarationen
grundsätzlich kein extern, kritisiere aber nicht diejenigen mit
diesbezüglich anderen Vorlieben. Letztendlich ist das eine Frage des
persönlichen Programmierstils, der sich bei Weitem nicht nur auf solche
Kleinigkeiten beschränkt.

———————————————
¹) Anders als bspw. Pascal erlaubt ISO-C keine verschachtelten
   Funktionsdefinitionen. In C gibt es diese Möglichkeit allenfalls als
   Spracherweiterung, wie bspw. beim GCC.

Beitrag #5877738 wurde von einem Moderator gelöscht.
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> In C ist jede Funktion extern in dem Sinn, dass sie außerhalb jeder
> anderen Funktion definiert wird¹. Das Schlüsselwort extern bezieht
> sich auf genau diese Eigenschaft und nicht – wie oft irrtümlicherweise
> angenommen – darauf, dass die deklarierte Funktion in einer anderen
> Übersetzungseinheit definiert ist.

Dass extern in C darauf zurückgeht, dass eine Funktion außerhalb aller 
anderen Funktionen definiert wurde, dem würde ich widersprechen. I.W 
bezieht sich extern auf die Linkage von Identifiern:

Objekte und Funktionen werden in C mit Identifiern bezeichnet, und der 
maximale Gültigkeitsbereich / Sichtbarkeit eines Identifiers ist eine 
Compilation  Unit.  Um von einer Compilation Unit auf eine in einer 
anderen Unit definierten Entity zuzugreifen, gibt es in C das Konzept 
der external Linkage.  Mit external Linkage werden quasi Identifier (auf 
Object-Ebene: Symbole) aus unterschiedlichen Compilation Units 
miteinander verbunden.

Weil der maximale Gültigkeitsbereich eines Identifiers eine Compilation 
Unit ist, wäre die natürliche Bedeutung von "void f(void);" internal 
Linkage, also wie bei "static void f(void);" :-)

> Ich persönlich halte davon dennoch nicht viel, da man die Unterscheidung
> der beiden o.g. Fälle ebenso gut durch die Verwendung bzw. das Weglassen
> von static erreicht.

Mit static hat man aber eine andere Linkage, und es ist nicht mehr 
möglich, die Entity aus anderen Compilation Units aus zu referenzieren. 
Man trifft damit also eine weitreichende Design-Entscheidung.

Zudem wird man eine extern Decl in den Header schreiben, also in die 
Interface-Beschreibung eines Moduls, eine static Decl aber im C-Modul 
selbst haben wollen. In einem Header kann eine static Decl sogar fatal 
sein, weil diese es unmöglich macht, in einer anderen Unit, die den 
Header verwendet, eine lokale Entity gleichen Namens aber anderen 
Interfaces zu definieren.

> M.W. gibt es in C nur einen einzigen Fall, wo extern in einer
> Funktionsdelaration einen semantischen Unterschied macht, nämlich in
> Verbindung mit einer inline-Deklaration:
>  
> Mit
>  
> inline void f(void) {
>   // ...
> }
>  
> wird eine so genannte "inline definition" der Funktion f angelegt. Damit
> kann die Funktion zwar geinlinet, aber weder per Call aufgerufen noch
> per Funktionszeiger referenziert werden.

Doch, das geht beides. inline ist eine höfliche Bitte an den Compiler, 
den Code zu inlinen.  Wird z.B. die Adresse der Funktion genommen, ist 
das kein Problem.  Ob eine Instanz der Funktion erstellt wird, und mit 
welcher Linkage, hängt mit der inline-Variante zusammen.

> Fügt man hingegen ein extern hinzu, erzeugt der Compiler eine
> "external definition", so dass f auch wie jede Nicht-Inline-Funktion
> genutzt werden kann:
>  
> extern inline void f(void) {
>   // ...
> }
>  
> Allerdings wird man diese Methode, eine external Definition zu erzeugen,
> aus den folgenden Gründen in der Praxis kaum nutzen:
>
> - Wird f nur in einer einzigen Übersetzungseinheit genutzt, wird man es
>   als static inline deklarieren. Dadurch wird ebenfalls eine external
>   Definition erzeugt, wobei der erzeugte Programmcode ggf. vom Compiler
>   wieder wegoptimiert wird.

Nö.  static inline hat internal Linkage, d.h. die Funktion kann von 
anderen Compilation Units nicht referenziert werden.  Hat man eine 
static inline Funktion in einem Header, dann erzeugt diese in den 
unterschiedlichen Units, die den Header verwenden, Funktionen, die 
formal nichts miteinander zu tun haben — sie haben lediglich den 
gleichen Code und den gleichen Namen und die gleiche (internal) Linkage.

Dass die Linkage internal ist, sieht man etwa wenn man folgendes 
Beispiel compiliert:
1
static inline void f (void) {}
2
3
void (*use (void)) (void)
4
{
5
    return f;
6
}

> - Wird f in mehreren Übersetzungseinheiten genutzt, schreibt man die
>   Inline-Definition von f in ein .h-File. Man kann f dort wie oben als
>   static inline deklarieren, oder man deklariert es nur als /inline/
>   und schreibt für die Erzeugung der external Definition  eine weitere
>   Deklaration in eines der .c-Files:
>
>   extern void f(void);

Schön verwirrend, weil hier eine Deklaration dazu führt, dass der 
Compiler die Funktion instanziiert :-)

Anwendungsfall wäre, wenn man stark davon ausgehen muss, dass gerne die 
Adresse der Funktion genommen wird (oder es andere Gründe gibt, warum 
nicht geinlinet wird wie Optimierung), und man dabei mehrere Instanzen 
wie bei static inline vermeiden will.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Johann L. schrieb:
> Dass extern in C darauf zurückgeht, dass eine Funktion außerhalb aller
> anderen Funktionen definiert wurde, dem würde ich widersprechen. I.W
> bezieht sich extern auf die Linkage von Identifiern:

Eine extern-Deklaration kann, muss sich aber nicht auf einen Identifier
mit external Linkage beziehen. Ein Beispiel hast du ja oben schon
genannt:

> Übrigens bedeutet das extern nicht notwendigerweise, dass function
> external Linkage hat, insbesondere wenn eine static Deklaration /
> Definition dem extern vorausgeht:
> static int f (void);
> extern int f (void);

Außerdem gab es das Schlüsselwort extern schon lange vor der Einführung
von static auf File-Scope und der damit verbundenen Begriffe external/
internal Linkage.

>> Ich persönlich halte davon dennoch nicht viel, da man die Unterscheidung
>> der beiden o.g. Fälle ebenso gut durch die Verwendung bzw. das Weglassen
>> von static erreicht.
>
> Mit static hat man aber eine andere Linkage, und es ist nicht mehr
> möglich, die Entity aus anderen Compilation Units aus zu referenzieren.

Genau das will man

Yalu X. schrieb:
> Bei Funktionen, die nur innerhalb einer einzigen Übersetzungseinheit
>   genutzt werden

ja auch gar nicht.

> Zudem wird man eine extern Decl in den Header schreiben, also in die
> Interface-Beschreibung eines Moduls, eine static Decl aber im C-Modul
> selbst haben wollen.

Klar, dem widerspreche ich auch überhaupt nicht.

>> Mit
>>
>> inline void f(void) {
>>   // ...
>> }
>>
>> wird eine so genannte "inline definition" der Funktion f angelegt. Damit
>> kann die Funktion zwar geinlinet, aber weder per Call aufgerufen noch
>> per Funktionszeiger referenziert werden.
>
> Doch, das geht beides. inline ist eine höfliche Bitte an den Compiler,
> den Code zu inlinen.

Wenn er dieser Bitte aber nicht entspricht und stattdessen einen Call
generiert, meckert bei obiger Deklaration der Linker das unaufgelöste
Symbol f an.

> Wird z.B. die Adresse der Funktion genommen

Das ergibt die gleiche Linkermeldung wie beim Call. Erst mit einer
static- oder extern-Deklaration wird aufrufbarer Code für die Funktion
erzeugt.

>> - Wird f nur in einer einzigen Übersetzungseinheit genutzt, wird man es
>>   als static inline deklarieren. Dadurch wird ebenfalls eine external
>>   Definition erzeugt, wobei der erzeugte Programmcode ggf. vom Compiler
>>   wieder wegoptimiert wird.
>
> Nö.  static inline hat internal Linkage, d.h. die Funktion kann von
> anderen Compilation Units nicht referenziert werden.

Deswegen schrieb ich ja

Yalu X. schrieb:
> Wird f nur in einer einzigen Übersetzungseinheit genutzt, ...

Oder schmeißt du jetzt "external Definition" unbd "external Linkage"
durcheinander? Das sind zwei völlig verschiedene Dinge. Eine mit static
definierte Funktion auf File-Scope hat internal Linkage, aber eine
external Definition. Das "external" bei "Linkage" bezieht sich auf die
Ausweitung auf andere Übersetzungseinheiten, das "external" bei der
"Definition" bedeutet aber "außerhalb jeder Funktion".

> Anwendungsfall wäre, wenn man stark davon ausgehen muss, dass gerne die
> Adresse der Funktion genommen wird (oder es andere Gründe gibt, warum
> nicht geinlinet wird wie Optimierung), und man dabei mehrere Instanzen
> wie bei static inline vermeiden will.

Genau so ist es.

von W.S. (Gast)


Lesenswert?

Ach ihr diskutiert ja noch immer - und gerade die Beiträge von Yalu 
werden immer schwurbeliger. "Extern in dem Sinne daß.. - in diesem Sinne 
ist jede.. bezieht sich auf..." und sonstige Schwurbeleien.

Mannomann, geht's noch?

Klar ist, daß C keine lokalen Funktionen und Prozeduren kennt wie z.B. 
Algol und Pascal sie seit Urzeiten kennen. Sowas ist ein Manko von C, 
aber man kann damit so lala leben. Ein Vorzug ist dieses Fehlen jedoch 
nicht gerade.

Mir kommt da so ein Honecker-Spruch (oder einer von Karl-Eduard von 
Knacks) in den Sinn: Diese Knallköpfe definierten "Freiheit" im Sinne 
von "Keimfreiheit" - also der Abwesenheit von dem, was jeder noch 
logisch denkende Mensch unter Freiheit versteht - naja, nur so lange wie 
die Mauer gehalten hat. Reisefreiheit = Abwesenheit der Möglichkeit zu 
reisen, gelle?

Und Yalu's Beiträge zum Thema "extern" klingen fast genau so. Eben ein 
Versuch der Sinnesverdrehung!

Nochmal: extern heißt schlichtweg: hier nicht vorrätig, sondern in einer 
anderen Quelle vorhanden - nämlich der, zu welcher diese Headerdatei 
gehört. Ohne diese einfach zu verstehende Grundbedeutung könnte man sich 
jegliche Headerdatei sonstwohin schmieren. Und nix von wegen "Extern im 
Sinne von.."



A. S. schrieb:
> Geht aber auch pragmatischer: da die Funktionsdeklaration auch ohne
> extern eindeutig ist, kann dies weggelassen werden.

Klar, man kann es in diesem speziellen Falle weglassen - aber SOLLTE 
man es in diesem speziellen Falle weglassen?

Ich sage dazu ein klares NEIN, denn das Ausmerzen von überflüssigen 
Besonderheiten und Extrawürsten hat gerade C nötig - man braucht einen 
Sonderfall weniger zu beachten.

Genau DARIN liegt auf lange Sicht der Fortschritt. So rein theoretisch 
ließe sich selbst C durchaus noch modernisieren und auf den Stand von 
2019 bringen - aber dazu müßte man eben genau so, wie damals ANSI mit 
der Machete durch's K&R-Gestrüpp hauen - ungeachtet des Gelabers der 
Leute, die mit schwülstigen Reden alte Designfehler als "besondere 
Bedeutung" den Leuten verkaufen wollen wie Yalu oder jedem, der den 
Mißstand kritisiert oder gar zumindest formal behebt, als unwissend zu 
bezeichnen - wie z.B. Rufus mal wieder:

Rufus Τ. F. schrieb:
> Da es bei Funktionen das Konzept der Vorwärtsdeklaration (durch den
> Funktionsprototypen) gibt, wird man diese kognitive Leistung wohl
> aufbringen müssen.

Nö, muß man überhaupt nicht. Stattdessen muß man - wenn es mit der 
Menschheit vorangehen soll - die besagte Machete zücken und den alten 
Unsinn heraushacken.

So herum!

Und:

Jawoll, es geht.
Ja, es geht tatsächlich mit allen mir bekannten Compilern.
Voila!

W.S.

von Rolf M. (rmagnus)


Lesenswert?

W.S. schrieb:
> Klar ist, daß C keine lokalen Funktionen und Prozeduren kennt wie z.B.
> Algol und Pascal sie seit Urzeiten kennen. Sowas ist ein Manko von C,
> aber man kann damit so lala leben. Ein Vorzug ist dieses Fehlen jedoch
> nicht gerade.

Wozu braucht man das? Ich hab's noch nie vermisst, und ich habs auch nie 
benutzt, als ich noch in Pascal programmiert habe.

> A. S. schrieb:
>> Geht aber auch pragmatischer: da die Funktionsdeklaration auch ohne
>> extern eindeutig ist, kann dies weggelassen werden.
>
> Klar, man kann es in diesem speziellen Falle weglassen - aber SOLLTE
> man es in diesem speziellen Falle weglassen?

Es ist kein "spezieller Fall", denn es geht nicht um eine einzelne, ganz 
bestimmte Funktion, sondern um alle.

> Ich sage dazu ein klares NEIN, denn das Ausmerzen von überflüssigen
> Besonderheiten und Extrawürsten hat gerade C nötig - man braucht einen
> Sonderfall weniger zu beachten.

Was ist daran "Sonderfall"? Ich schreibe eine Deklaration einer Funktion 
hin, und die gilt per Default als extern. Eigentlich gibt's nur zwei 
Fälle, nämlich Funktionen und Variablen. Und wenn schon, dann sind für 
mich die Variablen eher der Sonderfall, da es hier nur aus einem Grund 
nötig ist, das "extern" explizit hinzuschreiben: Um eine Definition 
überhaupt von einer reinen Deklaration unterschieden zu können. Denn 
ohne das "extern" wäre es eine Definition.

von (prx) A. K. (prx)


Lesenswert?

Rufus Τ. F. schrieb:
> Ich jedenfalls stehe dazu, daß ich "extern" bei Funktionsdeklarationen
> für überflüssig halte und daß ich der Ansicht bin, daß Leute, die es
> verwenden, nicht so recht verstanden haben, was es bedeutet.

Und andere Leute sind dazu eben anderer Ansicht. Ich habe schon oft 
"extern" explizit hingeschrieben, wenn es als eine Art 
Import-Deklaration gemeint war, die es in C leider nicht formal gibt. 
Analog dazu sehe ich es als Geburtsfehler von C an, dass Funktionen wie 
Daten implizit global sind, statt implizit auf die compilation unit 
bezogen.

von F. F. (foldi)


Lesenswert?

Hugo schrieb:
> Wenn du mal irgendwann Langeweile haben solltest, kannst du gerne mal
> ein C-Buch lesen ;-)

Und auf den ersten 100 Seiten steht es genau so da, wie Ralph es 
beschrieben hat.

von (prx) A. K. (prx)


Lesenswert?

Rolf M. schrieb:
> Und wenn schon, dann sind für
> mich die Variablen eher der Sonderfall, da es hier nur aus einem Grund
> nötig ist, das "extern" explizit hinzuschreiben: Um eine Definition
> überhaupt von einer reinen Deklaration unterschieden zu können. Denn
> ohne das "extern" wäre es eine Definition.

Wobei das aus historischen Gründen bei Variablen etwas komplizierter 
sein kann, wenn man reale Compiler in deren Default-Einstellung 
betrachtet. Die ein einfaches "int a;" in mehreren compilation units 
zulassen, und bei denen dies im Fall eines weiteren "int a = 1;" von 
einer verteilten Definition zu einer Deklaration wird.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Ralph S. schrieb:
> In file2.c verwendest du function(), die in file2.c nicht definiert ist.
> Wäre function() nicht als extern deklariert. würde der Compiler den Code
> für function() in der Datei file2.c suchen und dort nicht finden, weil
> nicht vorhanden. file2.c wäre somit nicht compilierbar.

Schon ausprobiert? ;-)

Ist mit GCC ohne Optionen compilierbar:
1
void f(void)
2
{
3
        function();
4
}
K&R-C lässt grüssen. Mit -Wall gibts "warning: implicit declaration of 
function ‘function’ [-Wimplicit function-declaration] function();".

von Dr. Sommer (Gast)


Lesenswert?

W.S. schrieb:
> Nochmal: extern heißt schlichtweg: hier nicht vorrätig, sondern in einer
> anderen Quelle vorhanden

Nochmal: Dies ist schlichtweg falsch.

Im C-Standard¹ auf S. 29 heißt es in §6.2.2-5:

> If the declaration of an identifier for a function has no storage-class 
specifier, its linkage is determined exactly as if it were declared with the 
storage-class specifier extern .

Das "extern" ist also immer impliziert, es sei denn man schreibt 
"static" oder "inline". Eine Deklaration ohne "static" und "inline", 
aber egal ob mit "extern" oder ohne, bedeutet immer, dass _genau 
eine_ Definition entweder in der selben oder einer anderen 
Translation Unit auftaucht.

W.S. schrieb:
> Diese Knallköpfe definierten "Freiheit" im Sinne
> von "Keimfreiheit" - also der Abwesenheit von dem, was jeder noch
> logisch denkende Mensch unter Freiheit versteht

Das war dann wohl von Orwell und Neusprech abgeschaut.

W.S. schrieb:
> Genau DARIN liegt auf lange Sicht der Fortschritt. So rein theoretisch
> ließe sich selbst C durchaus noch modernisieren und auf den Stand von
> 2019 bringen

Dann erhält man aber eine komplett andere Sprache, welche zu bestehenden 
Programmen und Tools inkompatibel ist. Solche Sprachen gibt es, wie z.B. 
Rust. Sie haben es sehr schwer, im Embedded-Umfeld akzeptiert zu werden, 
weil große vorhandene Codebasen, Tools und Standards (z.B. MISRA) C 
sind/voraussetzen.
Bei vielen Programmen/Bibliotheken/Tools heißt es doch sogar: ANSI-C 
kompatibel, d.h. zum allerersten offiziellen C-Standard von 1989. Selbst 
der gleichwertige, neuere "internationale" Name ISO-C90 wird ignoriert, 
und alle folgenden neueren Standards (C99, C11, C17) sowieso. Die 
Industrie scheint sich darauf geeinigt zu haben, dass ANSI-C die einzig 
wahre Programmiersprache ist und niemals etwas neues akzeptabel sein 
wird.

1: Draft: 
https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf

von (prx) A. K. (prx)


Lesenswert?

Die Verwirrung rührt u.A. auch daher, dass Ritchie ursprünglich "extern" 
ausschliesslich als Linkage vorsah, ohne Bezug auf Speicherzuordnung. 
Dadurch war "extern" ausserhalb von Funktionen vollständig redundant, 
egal ob bei Daten oder bei Funktionen.

In seinem Compiler reichte folglich
   extern int a;
aus, um Storage zu definieren. Das war in sich konsistent, aber nicht 
auf allen Plattformen implementierbar, weil es auf der Linker-Ebene 
analog zu FORTRAN COMMON Records unterstützt werden muss.

Andere Compiler-Autoren interpretierten C anders. Bei Daten änderte sich 
die Rolle von "extern" etwas, bei Funktionen blieb sie unverändert. 
ANSI-C legte sich in diese Richtung fest, verwarf Ritchies Prinzip.

Siehe C99 Rationale 6.2.2.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

W.S. schrieb:
> Und Yalu's Beiträge zum Thema "extern" klingen fast genau so. Eben ein
> Versuch der Sinnesverdrehung!
>
> Nochmal: extern heißt schlichtweg: hier nicht vorrätig, sondern in einer
> anderen Quelle vorhanden

Wie kommst du darauf? Es gibt in C keine Möglichkeit, explizit auf ein
Symbol in einer anderen Quelle zu verweisen, und schon gar nicht
geschieht dies mit extern.

Du kannst das im ISO-Dokument nachlesen, allerdings musst du dazu die
Informationen von mehreren Textstellen zusammentragen, da es keinen
Abschnitt speziell zu extern gibt, in dem dieses in allen Details
zusammenfassend beschrieben wird. Erschwerend kommt hinzu, dass das
Schlüsselwort extern weitere Funktionen hat, bspw. um eine reine
Deklaration von einer tentative Definition zu unterscheiden.

Um es dir etwas leichter zu machen, habe ich mal ein aus den Quellen
main.c und other.c bestehendes ISO-konformes Beispiel erstellt, aus dem
hervorgeht, dass du mit deiner obigen Vermutung daneben liegst:


main.c:
1
#include <stdio.h>
2
3
static void f(void) {
4
  printf("static f in main.c\n");
5
}
6
7
int main(void) {
8
  extern void f(void);
9
  f();
10
}


other.c:
1
#include <stdio.h>
2
3
void f(void) {
4
  printf("f in other.c\n");
5
}

Nach deiner Theorie müsste der Aufruf von f in main() zur Definition in
other.c führen. Tatsächlich wird aber das in main.c definierte f
aufgerufen. Das liegt daran, dass die extern-Deklaration von f auf eine
external Definition von f verweist. Davon gibt es im Programm zwei,
nämlich eine in main.c und eine in other.c. Dabei verdeckt das f in
main.c seinen Namensvetter in other.c, weswegen die extern-Deklaration
in main() auf das f in main.c verweist.

Am besten liest du dir dazu Abschnitt 6.9 der ISO-Norm durch, in der
neben anderen wichtigen Dingen auch folgendes steht:

1
As discussed in 5.1.1.1, the unit of program text after preprocessing is
2
a translation unit, which consists of a sequence of external
3
declarations. These are described as ‘‘external’’ because they appear
4
outside any function (and hence have file scope). As discussed in 6.7, a
5
declaration that also causes storage to be reserved for an object or a
6
function named by the identifier is a definition.

von (prx) A. K. (prx)


Lesenswert?

W.S. schrieb:
> Nochmal: extern heißt schlichtweg: hier nicht vorrätig, sondern in einer
> anderen Quelle vorhanden - nämlich der, zu welcher diese Headerdatei
> gehört.

Wobei diese andere Quelle auch die gleiche Quelle sein kann, wenn es 
nämlich um jene Quelle geht, die zum Include-File gehört. Nur kommt dann 
eben noch zusätzlich die Funktionsdefinition hinzu, wodurch im 
Include-File eine Vorwärtsdeklaration daraus wird, kein Verweis auf eine 
andere Quelle.

Soviel zu Sprachschwurbelei. Wenn es um formale Standards (oder 
juristische Aspekte) geht, ist exakte Ausdrucksweise wichtig, auch wenn 
es dann etwas komplexer klingen kann.

Ausserdem beziehst du dich auf jene Form der Modularisierung, die man 
beim Programmieren verwenden sollte, aber nicht auf die Sprache. Man 
sollte auseinander halten, ob es um praktische Programmierung geht, oder 
um die formale Sprache selbst.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

A. K. schrieb:
> Die Verwirrung rührt u.A. auch daher, dass Ritchie ursprünglich "extern"
> ausschliesslich als Linkage vorsah, ohne Bezug auf Speicherzuordnung.
> Dadurch war "extern" ausserhalb von Funktionen vollständig redundant,
> egal ob bei Daten oder bei Funktionen.

Ursprünglich gab es auf File-Scope für Funktionen gar kein extern. Das
wurde erst später als Option hinzugefügt, was offensichtlich keine so
gute Idee war, wie man an diesem Thread sieht :)

: Bearbeitet durch Moderator
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> markus schrieb:
> Warum sind die Funktionen als extern deklariert?
>
> Weil der Programmierer nicht wusste, dass "extern" bei Funktionen
> komplett wirkungslos ist.

Falsche Schlussfolgerung. Ich weiß, dass "extern" bei Funktionen 
komplett wirkungslos ist und schreibe es trotzdem hin.

Der Grund ist wahrscheinlich mein Hang zur Symmetrie. Hiermit meine ich 
die externe Variablendeklaration, wo das extern natürlich notwendig ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> As discussed in 5.1.1.1, the unit of program text after preprocessing
> is a translation unit, which consists of a sequence of external
> declarations. These are described as ‘‘external’’ because they appear
> outside any function (and hence have file scope).

M.E. sollte man diese Verwendung "external" besser schnell vergessen, 
wenn man jemand erklären will, was "extern" in C/C++ bedeutet oder 
external Linkage.

Diese Verwendung mag bei vergleichender Analyse unterscheidlicher 
Programmiersprachen hilfreich sein, aber in Kontexten wie im hiesigen 
Thread ist dieses "external" einfach nur ne Nebelbombe, die das 
Auseinanderdividieren von "extern" und external Linkage unnötig 
erschwert.

von W.S. (Gast)


Lesenswert?

Yalu X. schrieb:
> Ursprünglich gab es...

.. nicht mal Namen in den Argumenten der Funktionen.

Wir sollten froh sein, daß per ANSI-C dieser üble K&R-Mist ausgemistet 
worden ist. Also jammere nicht diesem Uralt-Zeugs hinterher und richt 
deinen Blick lieber nach vorn und auf den derzeitigen Zustand dieser 
Programmiersprache - da gibt es noch sehr viel auszumisten. Und das 
Herumgeeiere wegen 'extern', was du hier dazu geschrieben hast, gehört 
dazu.


Frank M. schrieb:
> Ich weiß, dass "extern" bei Funktionen
> komplett wirkungslos ist und schreibe es trotzdem hin.

Eins PLUS.

W.S.

von Yalu X. (yalu) (Moderator)


Lesenswert?

W.S. schrieb:
> Yalu X. schrieb:
>> Ursprünglich gab es...
>
> .. nicht mal Namen in den Argumenten der Funktionen.
>
> Wir sollten froh sein, daß per ANSI-C dieser üble K&R-Mist ausgemistet
> worden ist. Also jammere nicht diesem Uralt-Zeugs hinterher und richt
> deinen Blick lieber nach vorn und auf den derzeitigen Zustand dieser
> Programmiersprache

Ich jammere keinem Uralt-Zeugs hinterher, sondern bin mit dem aktuellen
Stand von C mehr zufrieden denn je. Ein paar Dinge (die nicht Gegenstand
dieses Threads sind) sind aus historischen Gründen zwar etwas krumm,
aber auch damit kann ich ganz gut leben.

> - da gibt es noch sehr viel auszumisten. Und das Herumgeeiere wegen
> 'extern', was du hier dazu geschrieben hast, gehört dazu.

Ich persönlich hätte überhaupt kein Problem damit, das extern komplett
auszumisten, nicht nur für Funktionen, sondern auch für Objekte. Es war
von Anfang an ein Work-Around, der in Verbindung mit den Erweiterungen
in späteren C-Versionen für viele immer unverständlicher wurde. Dabei
wird er auf den meisten aktuellen Plattformen gar nicht (mehr) benötigt.

Da du selber aber an dieser Altlast zu hängen scheinst, finde ich es
auch nicht schlimm, wenn das extern beibehalten wird. Ich weiß, wie
damit umzugehen ist, und das allein ist für mich wichtig. Und wenn du
Probleme damit hast¹, ist das zwar schade, aber zum Glück sind deine
Probleme nicht meine ;-)

—————————————
¹) und die scheinst du mit allem zu haben, was auch nur einen µm von
   deinem heißgeliebten Object-Pascal abweicht

von A. S. (Gast)


Lesenswert?

Yalu X. schrieb:
> Dabei wird er auf den meisten aktuellen Plattformen gar nicht (mehr) benötigt.
auch nicht für Variablen? Was wäre da der Ersatz für extern? Oder 
meintest Du das Beispiel mit den Funktionen f und extern f?

von Yalu X. (yalu) (Moderator)


Lesenswert?

A. S. schrieb:
> Yalu X. schrieb:
>> Dabei wird er auf den meisten aktuellen Plattformen gar nicht (mehr) benötigt.
> auch nicht für Variablen? Was wäre da der Ersatz für extern? Oder
> meintest Du das Beispiel mit den Funktionen f und extern f?

Nimm ein beliebiges C-Programm, entferne darin sämtliche extern (auch
in Variablendeklarationen), baue es neu, führe es aus und wundere dich.

ABER: Aus Portabilitätsgründen solltest du dich keineswegs darauf
verlassen, dass das immer so ist. Es sind zwar offiziell mehrere gleiche
temptative Definitions (d.h. globale Variablendeklarationen ohne
extern und ohne Initialisierung) innerhalb einer Übersetzungseinheit
erlaubt, nicht aber übergreifend über mehrere Übersetzungseinheiten.
Immerhin ist es im C11-Standard unter "Common extensions" aufgeführt, so
dass eine gewisse Chance besteht, dass es in einer zukünftigen C-Version
offiziell wird.

von (prx) A. K. (prx)


Lesenswert?

Yalu X. schrieb:
> Nimm ein beliebiges C-Programm, entferne darin sämtliche extern (auch
> in Variablendeklarationen), baue es neu, führe es aus und wundere dich.

Auch hier? ;-)
1
int f(void)
2
{
3
    extern int a;
4
    return a;
5
}
Das ist die wohl einzige Stelle, an der "extern" immer notwendig ist. 
Allerdings ist diese Art Code etwas schräg.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

A. K. schrieb:
> Auch hier? ;-)
> int f(void)
> {
>     extern int a;
>     return a;
> }

Stimmt, da kann man das extern nicht weglassen.

> Das ist die wohl einzige Stelle, an der "extern" immer notwendig ist.

Die andere ist die Kombination mit inline:

Yalu X. schrieb:
> extern inline void f(void) {
>   // ...
> }

A. K. schrieb:
> Allerdings ist diese Art Code etwas schräg.

Die direkte Kombination mit dem inline ebenfalls. Beides wird man in
realem Code wohl so gut wie nie antreffen.

von Dr. Sommer (Gast)


Lesenswert?

Stellt euch vor, es ist 2187. Der Warp-Antrieb wurde entwickelt. Die 
Menschheit erkundet Alpha Centauri. Alle Krankheiten können geheilt 
werden. Der letzte Windows XP-Rechner wird abgeschaltet. Es gibt keine 
COBOL-Programme mehr. Und schließlich wird ein C-und-C++-Compiler 
entwickelt, welcher keinerlei (Vorwärts-)Deklarationen mehr benötigt - 
alle Programm-Elemente werden nur einmal in der .c/.cpp-Datei definiert, 
Header-Dateien gibt es nicht mehr, der Compiler wird per
1
cc *.c *.cpp
aufgerufen, genau wie jetzt der "javac". Man darf noch träumen!

von Uhu U. (uhu)


Lesenswert?

Yalu X. schrieb:
> void f(void);
>> Mit der ersten drückt man (vereinfacht) aus:
>
>   "Ich möchte die Funktion f benutzen."

Nö, das heißt nur, dass die Funktion f keinen Parameter hat und kein 
Ergebnis zurück gibt.

Das f muss noch nichtmal definiert sein und von "benutzen" redet schon 
gar keiner…

von Schamane (Gast)


Lesenswert?

Yalu X. schrieb:

> int main(void) {
>   extern void f(void);

Wusste gar nicht dass man in Funktionen andere Funktionen deklarieren 
kann. Habe ich noch nie gesehen und macht wohl auch keiner aber dass das 
überhaupt ohne Fehlermeldung compiliert hätte ich nicht gedacht.

von Joachim B. (jar)


Lesenswert?

Yalu X. schrieb:
> Stimmt, da kann man das extern nicht weglassen.

ihr macht mich fertig :)
aber egal man lernt ja immer dazu.....

Ich hatte nie Funktionen in Header mit extern benannt, las aber irgendwo 
das es "sauberer" sein soll, ich frag mich immer noch wieso?

In Header stehen ja keine Funktionen, nun bin ich gerade wieder mal am 
programmieren, zu warm zum Löten und stolpere über meine extern 
deklarierten Funktionen.
Also lösche ich alle extern vor Funktionen im Header und freue mich das 
es nun später Tipparbeit spart.

von (prx) A. K. (prx)


Lesenswert?

Dr. Sommer schrieb:
> Stellt euch vor, es ist 2187. ... schließlich wird ein C-und-C++-Compiler
> entwickelt, welcher keinerlei (Vorwärts-)Deklarationen mehr benötigt -
> alle Programm-Elemente werden nur einmal in der .c/.cpp-Datei definiert,

So lange sich das auf deinen eigenen Quelltext bezieht, wäre das heute 
schon kein Problem. Compiler wie IBMs VisualAge C/C++ näherten sich dem 
bereits vor vielen Jahren. Aber meistens gibt es noch weiteren Code, von 
dem kein Quelltext vorliegt, also Libraries und Betriebssystem. 
Irgendwelche Interface-Definitionen sind also unverzichtbar.

von Dr. Sommer (Gast)


Lesenswert?

A. K. schrieb:
> Irgendwelche Interface-Definitionen sind also unverzichtbar.

Java kommt auch ohne aus...

von (prx) A. K. (prx)


Lesenswert?

Dr. Sommer schrieb:
> A. K. schrieb:
>> Irgendwelche Interface-Definitionen sind also unverzichtbar.
>
> Java kommt auch ohne aus...

Wenn man den Quellcode von Software nicht hat, aber deren Funktionalität 
verwenden will, dann muss für den Compiler irgendwo beschrieben sein, 
wie das geht. Wenn mir der letzte Java-Update zur Allwissenheit nicht 
entgangen ist, dann muss es also eine Interface-Beschreibung geben. Wie 
auch immer die heisst.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

A. K. schrieb:
> Wenn mir der letzte Java-Update zur Allwissenheit nicht
> entgangen ist, dann muss es also eine Interface-Beschreibung geben. Wie
> auch immer die heisst.

Die ist bei java immer in den binären .class (.jar) Dateien enthalten. 
Wenn das mal bei .lib/.so/.a/.dll Dateien auch so wäre...

von (prx) A. K. (prx)


Lesenswert?

Dr. Sommer schrieb:
> Die ist bei java immer in den binären .class (.jar) Dateien enthalten.
> Wenn das mal bei .lib/.so/.a/.dll Dateien auch so wäre...

Na also. Es gibt weiterhin eine Interface-Definition, nur eben nicht als 
getrenntes File auf Betriebssystem-Ebene.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

A. K. schrieb:
>
1
> int f(void)
2
> {
3
>     extern int a;
4
>     return a;
5
> }
>
> Das ist die wohl einzige Stelle, an der "extern" immer notwendig ist.
> Allerdings ist diese Art Code etwas schräg.

Aber nicht ohne Anwendungsfall, zum Beispiel wenn man ein Symbol 
verwenden will, das im Linkerskript definiert wird oder per --defsym auf 
der Komandozeile.  Vorstellbar etwa wenn man eine Checksumme über den 
Programmcode berechnen will:
1
int crc (void)
2
{
3
    extern byte __code_start[];
4
    extern byte __code_end[];
5
    ...
6
}

Allerdings könnte man das Zeig ebenso vor der Funktion deklarieren, ich 
seh da jetzt kein Killerargument.  Zwar bleiben die Deklarationen dann 
auch hinter crc() gültig, aber man wird eh keine anderen, kollidierenden 
Deklarationen haben.

von W.S. (Gast)


Lesenswert?

Yalu X. schrieb:
> Ich persönlich hätte überhaupt kein Problem damit, das extern komplett
> auszumisten, nicht nur für Funktionen, sondern auch für Objekte.

Ach ja: Objekte in C.

Yalu X. schrieb:
> Nimm ein beliebiges C-Programm, entferne darin sämtliche extern (auch
> in Variablendeklarationen), baue es neu, führe es aus und wundere dich.

Du Witzbold!
Wenn schon, dann auch alle #include's entfernen, denn genau dort 
findest du all die von dir verachteten 'extern's.

Dein Herumgeeiere geht offenbar in die nächste Runde. Jetzt fänst du 
schon an, bei C von Objekten zu reden.

W.S.

von Rolf M. (rmagnus)


Lesenswert?

W.S. schrieb:
> Yalu X. schrieb:
>> Ich persönlich hätte überhaupt kein Problem damit, das extern komplett
>> auszumisten, nicht nur für Funktionen, sondern auch für Objekte.
>
> Ach ja: Objekte in C.

Was ist damit?

> Dein Herumgeeiere geht offenbar in die nächste Runde. Jetzt fänst du
> schon an, bei C von Objekten zu reden.

Offenbar weißt du nicht, was C unter Objekten versteht. Das macht ja 
erstmal nichts, aber dann solltest du dieses Unwissen nicht ganz so 
offen zur Schau stellen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

W.S. schrieb:
> Jetzt fänst du schon an, bei C von Objekten zu reden.

Die Vokabel ist nicht unüblich.

von Rolf M. (rmagnus)


Lesenswert?

Nicht nur nicht unüblich, sondern auch in ISO C definiert.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.