So verwenden Sie das Schlüsselwort extern in C.

235

Meine Frage ist, wann eine Funktion mit dem externSchlüsselwort in C referenziert werden soll .

Ich sehe nicht ein, wann dies in der Praxis angewendet werden sollte. Während ich ein Programm schreibe, werden alle von mir verwendeten Funktionen über die von mir enthaltenen Header-Dateien verfügbar gemacht. Warum sollte es also nützlich sein, externauf etwas zuzugreifen, das nicht in der Header-Datei verfügbar war?

Ich könnte darüber nachdenken, wie es externfalsch funktioniert, und wenn ja, korrigieren Sie mich bitte.

Bearbeiten: Sollten Sie externetwas tun , wenn es sich um die Standarddeklaration ohne das Schlüsselwort in einer Header-Datei handelt?

lillq
quelle
Verwandte Funktionen: stackoverflow.com/questions/856636/… für Variablen: stackoverflow.com/questions/1433204
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Antworten:

290

" extern" ändert die Verknüpfung. Mit dem Schlüsselwort wird angenommen, dass die Funktion / Variable an einer anderen Stelle verfügbar ist, und die Auflösung wird auf den Linker verschoben.

Es gibt einen Unterschied zwischen "extern" bei Funktionen und bei Variablen: Bei Variablen wird die Variable selbst nicht instanziiert, dh es wird kein Speicher zugewiesen. Dies muss woanders gemacht werden. Daher ist es wichtig, wenn Sie die Variable von einem anderen Ort importieren möchten. Bei Funktionen teilt dies dem Compiler nur mit, dass die Verknüpfung extern ist. Da dies die Standardeinstellung ist (Sie verwenden das Schlüsselwort "static", um anzuzeigen, dass eine Funktion nicht über eine externe Verknüpfung gebunden ist), müssen Sie sie nicht explizit verwenden.

Bluebrother
quelle
1
Warum gibt es dann dasselbe externe Ding in Git
?
K & R bemerkt nicht, dass es standardmäßig die Funktion als "extern" deklariert, aber diese Antwort löst meine Verwirrung!
Acgtyrant
@rsjethani Ich denke, es ist, das Dokument strenger und formatierter zu machen.
Acgtyrant
Vielleicht eine dumme Frage, aber wie ist das im Vergleich zur Vorwärtserklärung?
weberc2
196

extern teilt dem Compiler mit, dass diese Daten irgendwo definiert sind und mit dem Linker verbunden werden.

Mit Hilfe der Antworten hier und im Gespräch mit einigen Freunden hier ist das praktische Beispiel für die Verwendung von extern .

Beispiel 1 - um eine Falle zu zeigen:

File stdio.h:

int errno;
/* other stuff...*/

myCFile1.c:
#include <stdio.h>

Code...

myCFile2.c:
#include <stdio.h>

Code...

Wenn myCFile1.o und myCFile2.o verknüpft sind, enthält jede der c-Dateien separate Kopien von errno . Dies ist ein Problem, da in allen verknüpften Dateien dieselbe Fehlernummer verfügbar sein soll.

Beispiel 2 - Das Update.

File stdio.h:

extern int errno;
/* other stuff...*/

File stdio.c

int errno;

myCFile1.c:
#include <stdio.h>

Code...

myCFile2.c:
#include <stdio.h>

Code...

Wenn nun sowohl myCFile1.o als auch MyCFile2.o durch den Linker verknüpft sind, zeigen beide auf denselben Fehler . Somit wird die Implementierung mit extern gelöst .

lillq
quelle
70
Das Problem ist nicht, dass die Module myCFile1 und myCFile2 eine separate Kopie von errno haben, sondern dass beide ein Symbol namens "errno" anzeigen. Wenn der Linker dies sieht, weiß er nicht, welche "errno" er auswählen soll, daher wird er mit einer Fehlermeldung gerettet.
Cwick
2
Was bedeutet eigentlich "vom Linker verlinkt"? Jeder benutzt diesen Begriff, ich finde keine Definition :(
Marcel Falliere
7
@MarcelFalliere Wiki ~ Der Compiler kompiliert jede Quelldatei für sich und erstellt für jede Quelldatei eine Objektdatei. Der Linker verknüpft diese Objektdateien mit einer ausführbaren Datei.
Bitterblue
1
@cwick gcc gibt auch nach der Verwendung von -Wallund keinen Fehler oder keine Warnung aus -pedantic. Warum ? und wie ?
b-ak
6
Schützt ein Include Guard nicht vor genau dieser Sache?
obskyr
32

Es wurde bereits festgestellt, dass das externSchlüsselwort für Funktionen redundant ist.

Variablen, die von Kompilierungseinheiten gemeinsam genutzt werden, sollten in einer Header-Datei mit dem Schlüsselwort extern deklariert und dann in einer einzigen Quelldatei ohne das Schlüsselwort extern definiert werden. Die Single-Source-Datei sollte diejenige sein, die den Namen der Header-Datei teilt.

aib
quelle
@aib "redundant für Funktionen", überprüfen Sie meinen Kommentar in der Antwort von bluebrother.
Rsjethani
Was ist, wenn Sie keine der Funktionen in der Header-Datei verfügbar machen möchten? Wäre es nicht besser, die Variable in einer C-Datei zu deklarieren und in einer anderen Datei extern darauf zuzugreifen? Lassen Sie den Linker das Problem lösen und den Rest des Headers ausblenden.
Ste3e
16

Viele Jahre später entdecke ich diese Frage. Nachdem ich jede Antwort und jeden Kommentar gelesen hatte, dachte ich, ich könnte ein paar Details klären ... Dies könnte für Leute nützlich sein, die über die Google-Suche hierher kommen.

Die Frage bezieht sich speziell auf die Verwendung von "extern" -Funktionen, daher werde ich die Verwendung von "extern" mit globalen Variablen ignorieren.

Definieren wir 3 Funktionsprototypen:

//--------------------------------------
//Filename: "my_project.H"
extern int function_1(void);
static int function_2(void);
       int function_3(void);

Die Header-Datei kann vom Hauptquellcode wie folgt verwendet werden:

//--------------------------------------
//Filename: "my_project.C"
#include "my_project.H"

void main(void){
    int v1 = function_1();
    int v2 = function_2();
    int v3 = function_3();
}

int function_2(void) return 1234;

Um zu kompilieren und zu verknüpfen, müssen wir "function_2" in derselben Quellcodedatei definieren, in der wir diese Funktion aufrufen. Die beiden anderen Funktionen können in unterschiedlichem Quellcode " .C" definiert sein oder sich in einer beliebigen Binärdatei ( .OBJ, * .LIB, * .DLL) befinden, für die wir möglicherweise nicht den Quellcode haben.

Fügen wir den Header "my_project.H" erneut in eine andere "* .C" -Datei ein, um den Unterschied besser zu verstehen. Im selben Projekt fügen wir die folgende Datei hinzu:

//--------------------------------------
//Filename: "my_big_project_splitted.C"
#include "my_project.H"

void old_main_test(void){
    int v1 = function_1();
    int v2 = function_2();
    int v3 = function_3();
}

int function_2(void) return 5678;

int function_1(void) return 12;
int function_3(void) return 34;

Wichtige Merkmale zu beachten:

  • Wenn eine Funktion in einer Header-Datei als "statisch" definiert ist, muss der Compiler / Linker in jedem Modul, das diese Include-Datei verwendet, eine Instanz einer Funktion mit diesem Namen finden.

  • Eine Funktion, die Teil der C-Bibliothek ist, kann in nur einem Modul ersetzt werden, indem ein Prototyp nur in diesem Modul mit "statisch" neu definiert wird. Ersetzen Sie beispielsweise einen Aufruf von "malloc" und "free", um die Funktion zur Erkennung von Speicherverlusten hinzuzufügen.

  • Der Bezeichner "extern" wird für Funktionen nicht wirklich benötigt. Wenn "statisch" nicht gefunden wird, wird eine Funktion immer als "extern" angenommen.

  • "Extern" ist jedoch nicht die Standardeinstellung für Variablen. Normalerweise muss jede Header-Datei, die Variablen definiert, die in vielen Modulen sichtbar sein sollen, "extern" verwenden. Die einzige Ausnahme wäre, wenn garantiert wird, dass eine Header-Datei von einem und nur einem Modul enthalten ist.

    Viele Projektmanager würden dann verlangen, dass eine solche Variable am Anfang des Moduls platziert wird und nicht in einer Header-Datei. Einige große Projekte, wie der Videospielemulator "Mame", erfordern sogar, dass eine solche Variable nur über der ersten Funktion angezeigt wird, die sie verwendet.

Christian Gingras
quelle
Warum genau braucht eine statische Funktion eine Definition gegenüber den externen? (Ich weiß, dass dies 2 Jahre zu spät ist, aber dies ist wirklich sehr hilfreich für das Verständnis)
SubLock69
2
Die Definition wird benötigt, wenn Sie die Funktion in Zeile 100 aufrufen und in Zeile 500 instanziieren. Zeile 100 würde einen undefinierten Prototyp deklarieren. Sie fügen also den Prototyp oben hinzu.
Christian Gingras
15

In C wird 'extern' für Funktionsprototypen impliziert, da ein Prototyp eine Funktion deklariert, die an einer anderen Stelle definiert ist. Mit anderen Worten, ein Funktionsprototyp verfügt standardmäßig über eine externe Verknüpfung. Die Verwendung von 'extern' ist in Ordnung, aber redundant.

(Wenn eine statische Verknüpfung erforderlich ist, muss die Funktion sowohl im Prototyp als auch im Funktionsheader als 'statisch' deklariert werden. Diese sollten sich normalerweise beide in derselben C-Datei befinden.)

Steve Melnikoff
quelle
8

Ein sehr guter Artikel, in dem ich über das externSchlüsselwort gekommen bin , zusammen mit den Beispielen: http://www.geeksforgeeks.org/understanding-extern-keyword-in-c/

Ich stimme jedoch nicht zu, dass die Verwendung externin Funktionsdeklarationen überflüssig ist. Dies soll eine Compilereinstellung sein. Daher empfehle ich, die externin den Funktionsdeklarationen zu verwenden, wenn dies erforderlich ist.

tozak
quelle
3
Ich habe den Artikel von geeksforgeeks.org gelesen, bevor ich hierher kam, fand ihn aber ziemlich schlecht geschrieben. Abgesehen von den grammatikalischen und syntaktischen Mängeln werden viele Wörter verwendet, um denselben Punkt mehrmals hervorzuheben und dann wichtige Informationen zu überfliegen. Zum Beispiel ist in Beispiel 4 plötzlich 'somefile.h' enthalten, aber es wird nichts anderes darüber gesagt als: "Angenommen, somefile.h hat die Definition von var". Nun, die Informationen, die wir "annehmen", sind zufällig die Informationen, nach denen ich suche. Leider sind keine der Antworten auf dieser Seite viel besser.
Elise van Looij
6

Wenn jede Datei in Ihrem Programm zuerst zu einer Objektdatei kompiliert wird, werden die Objektdateien miteinander verknüpft extern. Es sagt dem Compiler: "Diese Funktion existiert, aber der Code dafür ist woanders. Keine Panik."

Chris Lutz
quelle
So wird normalerweise übersetzt: Quelldateien werden zu Objektdateien kompiliert und dann verknüpft. Wann brauchen Sie in diesem Fall kein externes? Sie würden auch nicht #include verwenden, um Funktionen abzurufen, sondern Funktionsprototypen. Ich verstehe nicht, wovon du sprichst.
David Thornley
Ich habe in letzter Zeit das Problem, Dinge falsch zu lesen. Das tut mir leid. Als ich neu in C war, habe ich "file.c" # eingeschlossen, um nur die Funktionen in einer Datei direkt in die andere Datei aufzunehmen. Dann habe ich herausgefunden, wie man 'extern' benutzt. Ich dachte, er macht den gleichen Fehler wie ich.
Chris Lutz
4

Alle Deklarationen von Funktionen und Variablen in Header-Dateien sollten sein extern.

Ausnahmen von dieser Regel sind Inline-Funktionen, die im Header definiert sind, und Variablen, die - obwohl im Header definiert - lokal für die Übersetzungseinheit sein müssen (die Quelldatei, in die der Header aufgenommen wird): Dies sollten sein static.

In Quelldateien externsollte nicht für Funktionen und Variablen verwendet werden, die in der Datei definiert sind. Stellen Sie lokalen Definitionen einfach voran staticund tun Sie nichts für gemeinsam genutzte Definitionen - sie sind standardmäßig externe Symbole.

Der einzige Grund für die Verwendung externin einer Quelldatei besteht darin, Funktionen und Variablen zu deklarieren, die in anderen Quelldateien definiert sind und für die keine Header-Datei bereitgestellt wird.


Das Deklarieren von Funktionsprototypen externist eigentlich nicht erforderlich. Einige Leute mögen es nicht, weil es nur Platz verschwendet und Funktionsdeklarationen bereits dazu neigen, Liniengrenzen zu überschreiten. Andere mögen es, weil auf diese Weise Funktionen und Variablen auf die gleiche Weise behandelt werden können.

Christoph
quelle
Können Sie einen Grund angeben, warum "Alle Deklarationen von Funktionen und Variablen in Header-Dateien extern sein sollten"? Aus den anderen Antworten geht hervor, dass sie standardmäßig extern sind.
lillq
@Lane: externist optional für Funktionsdeklarationen, aber ich behandle Variablen und Funktionen gerne gleich - zumindest ist das das Vernünftigste, was ich mir einfallen lassen kann, da ich mich nicht genau erinnere, warum ich damit angefangen habe;)
Christoph
Ist es nicht eine bessere Idee, immer globale Variablen in die C-Datei aufzunehmen, damit sie nicht von anderen zufälligen C-Dateien gesehen werden, die den Header enthalten? Und aus Gründen der Klarheit immer extern auf jedem globalen zu verwenden, außer auf der initialisierten wahren Senke; Wenn es extern vorangestellt ist, wird es an anderer Stelle definiert.
Ste3e
3

In anderen Quelldateien tatsächlich definierte Funktionen sollten nur in Headern deklariert werden . In diesem Fall sollten Sie extern verwenden, wenn Sie den Prototyp in einem Header deklarieren .

In den meisten Fällen sind Ihre Funktionen eine der folgenden (eher eine bewährte Methode):

  • statisch (normale Funktionen, die außerhalb dieser .c-Datei nicht sichtbar sind)
  • statische Inline (Inlines aus .c- oder .h-Dateien)
  • extern (Deklaration in Headern der nächsten Art (siehe unten))
  • [überhaupt kein Schlüsselwort] (normale Funktionen, auf die über externe Deklarationen zugegriffen werden soll)
Eduard - Gabriel Munteanu
quelle
Warum sollten Sie extern sein, wenn Sie den Prototyp deklarieren, wenn dies die Standardeinstellung ist?
lillq
@ Lane: Könnte ein bisschen voreingenommen sein, aber jedes vernünftige Projekt, an dem ich gearbeitet habe, verwendet die folgende Konvention: Deklarieren Sie in Headern Prototypen nur für externe Funktionen (daher extern). In .c-Dateien können einfache Prototypen verwendet werden, um die Notwendigkeit einer bestimmten Reihenfolge zu vermeiden. Sie sollten jedoch nicht in Kopfzeilen platziert werden.
Eduard - Gabriel Munteanu
1

Wenn Sie diese Funktion in einer anderen DLL oder Bibliothek definiert haben, so dass der Compiler auf den Linker zurückgreift, um sie zu finden. Ein typischer Fall ist, wenn Sie Funktionen über die Betriebssystem-API aufrufen.

Otávio Décio
quelle