extern int ether_hostton (__const char *__hostname, struct ether_addr *__addr)
__THROW;
Ich habe die obige Funktionsdefinition in /usr/include/netinet/ether.h auf einer Linux-Box gefunden.
Kann jemand erklären, was die doppelten Unterstriche vor const (Schlüsselwort), addr (Bezeichner) und zuletzt __THROW bedeuten.
In C sind Symbole, die mit einem Unterstrich beginnen, gefolgt von entweder einem Großbuchstaben oder einem weiteren Unterstrich, für die Implementierung reserviert. Sie als Benutzer von C sollten keine Symbole erstellen, die mit den reservierten Sequenzen beginnen. In C++ ist die Einschränkung strenger; Sie als Benutzer dürfen kein Symbol erstellen, das einen doppelten Unterstrich enthält.
Gegeben:
extern int ether_hostton (__const char *__hostname, struct ether_addr *__addr)
__THROW;
Das __const
Notation ist da, um die Möglichkeit (etwas unwahrscheinlich) zu berücksichtigen, dass ein Compiler, mit dem dieser Code verwendet wird, Prototyp-Notationen unterstützt, aber das C89-Standardschlüsselwort nicht richtig versteht const
. Das autoconf
Makros können immer noch prüfen, ob der Compiler funktionierende Unterstützung für hat const
; Dieser Code könnte mit einem defekten Compiler verwendet werden, der diese Unterstützung nicht bietet.
Die Verwendung von __hostname
und __addr
ist eine Schutzmaßnahme für Sie als Nutzer des Headers. Wenn Sie mit GCC kompilieren und die -Wshadow
Option wird der Compiler Sie warnen, wenn lokale Variablen eine globale Variable überschatten. Wird die Funktion gerade genutzt hostname
Anstatt von __hostname
und ob Sie eine aufgerufene Funktion hatten hostname()
, würde es eine Abschattung geben. Durch die Verwendung von Namen, die für die Implementierung reserviert sind, gibt es keinen Konflikt mit Ihrem legitimen Code.
Die Verwendung von __THROW
bedeutet, dass der Code unter Umständen mit einer Art ‘Throw-Spezifikation’ deklariert werden kann. Dies ist kein Standard-C; es ist eher wie C++. Aber der Code kann mit einem C-Compiler verwendet werden, solange einer der Header (oder der Compiler selbst) definiert __THROW
auf leer oder auf eine Compiler-spezifische Erweiterung der Standard-C-Syntax.
Abschnitt 7.1.3 der C-Norm (ISO 9899:1999) sagt:
7.1.3 Reservierte Kennungen
Jeder Header deklariert oder definiert alle Identifikatoren, die in seinem zugehörigen Unterabschnitt aufgeführt sind, und deklariert oder definiert optional Identifizierer, die in seinem zugehörigen Unterabschnitt für zukünftige Bibliotheksanweisungen aufgeführt sind, und Identifizierer, die immer entweder für jegliche Verwendung oder zur Verwendung als Dateibereichsidentifizierer reserviert sind.
— Alle Bezeichner, die mit einem Unterstrich und entweder einem Großbuchstaben oder einem anderen Unterstrich beginnen, sind immer für jegliche Verwendung reserviert.
— Alle Bezeichner, die mit einem Unterstrich beginnen, sind immer für die Verwendung als Bezeichner mit Dateibereich sowohl im normalen als auch im Tag-Namensraum reserviert.
— Jeder Makroname in einem der folgenden Unterabschnitte (einschließlich der zukünftigen Bibliotheksanweisungen) ist für die Verwendung wie angegeben reserviert, wenn einer der zugehörigen Header enthalten ist; sofern nicht ausdrücklich anders angegeben (siehe 7.1.4).
— Alle Identifikatoren mit externer Verknüpfung in einem der folgenden Unterabschnitte (einschließlich der zukünftigen Bibliotheksanweisungen) sind immer der Verwendung als Identifikatoren mit externer Verknüpfung vorbehalten.154)
— Jeder Bezeichner mit Dateibereich, der in einem der folgenden Unterabschnitte (einschließlich der zukünftigen Bibliotheksanweisungen) aufgeführt ist, ist für die Verwendung als Makroname und als Bezeichner mit Dateibereich im selben Namensraum reserviert, wenn einer der zugehörigen Header enthalten ist.
Keine anderen Identifikatoren sind reserviert. Wenn das Programm einen Bezeichner in einem Kontext deklariert oder definiert, in dem er reserviert ist (anders als gemäß 7.1.4 zulässig), oder einen reservierten Bezeichner als Makronamen definiert, ist das Verhalten undefiniert.
Wenn das Programm entfernt (mit #undef
) jede Makrodefinition eines Bezeichners in der ersten oben aufgeführten Gruppe, ist das Verhalten undefiniert.
Fußnote 154) Die Liste der reservierten Kennungen mit externer Verknüpfung umfasst errno
, math_errhandling
,
setjmp
und va_end
.
Siehe auch Welche Regeln gelten für die Verwendung eines Unterstrichs in einem C++-Bezeichner? Viele der gleichen Regeln gelten für C und C++, obwohl die eingebettete Regel für doppelte Unterstriche nur in C++ gilt, wie oben in dieser Antwort erwähnt.
C99 Begründung
Das C99 Begründung sagt:
7.1.3 Reservierte Kennungen
Um den Implementierern maximalen Spielraum beim Packen von Bibliotheksfunktionen in Dateien zu geben, werden alle von der Bibliothek definierten externen Kennungen in einer gehosteten Umgebung reserviert. Dies bedeutet in der Tat, dass keine vom Benutzer bereitgestellten externen Namen mit Bibliotheksnamen übereinstimmen dürfen. auch nicht, wenn die Benutzerfunktion die gleiche Spezifikation hat. So z.B. strtod
kann im selben Objektmodul wie definiert werden printf
, ohne zu befürchten, dass Verbindungszeitkonflikte auftreten. Gleichermaßen, strtod
darf anrufen printf
oder printf
darf anrufen strtod
aus welchen Gründen auch immer, ohne Angst, dass die falsche Funktion aufgerufen wird.
Ebenfalls dem Implementierer vorbehalten sind alle externe Bezeichner, die mit einem Unterstrich beginnen, und alle anderen Bezeichner, die mit einem Unterstrich beginnen, gefolgt von einem Großbuchstaben oder einem Unterstrich. Dies gibt einen Namensraum zum Schreiben der zahlreichen nicht-externen Makros und Funktionen hinter den Kulissen, die eine Bibliothek benötigt, um ihre Arbeit richtig zu erledigen.
Mit diesen Ausnahmen versichert der Standard dem Programmierer dies alle anderen Identifikatoren sind verfügbar, ohne Angst vor unerwarteten Kollisionen beim Verschieben von Programmen von einer Implementierung in eine andere5. Beachten Sie insbesondere, dass ein Teil des Namensraums interner Bezeichner, der mit Unterstrich beginnt, dem Benutzer zur Verfügung steht: Übersetzer-Implementierer waren nicht die einzigen, die Verwendung für „versteckte“ Namen gefunden haben. C ist in vielerlei Hinsicht eine so portable Sprache, dass das Problem der „Verschmutzung des Namensraums“ eines der Haupthindernisse für das Schreiben von vollständig portierbarem Code war und ist. Daher stellt der Standard sicher, dass Makro- und typedef
Namen sind nur dann reserviert, wenn der zugehörige Header explizit enthalten ist.
5 Siehe §6.2.1 für eine Diskussion einiger Vorsichtsmaßnahmen, die ein Implementierer treffen sollte, um dieses Versprechen zu halten. Beachten Sie auch, dass alle implementierungsdefinierten Mitgliedsnamen in Strukturen, die in definiert sind <time.h>
und <locale.h>
muss mit einem Unterstrich beginnen, anstatt dem Muster anderer Namen in diesen Strukturen zu folgen.
Und der relevante Teil der Begründung für §6.2.1 Geltungsbereich von Identifikatoren ist:
Obwohl der Gültigkeitsbereich eines Bezeichners in einem Funktionsprototyp an seiner Deklaration beginnt und am Ende des Deklarators dieser Funktion endet, wird dieser Gültigkeitsbereich vom Präprozessor ignoriert. Somit wird ein Bezeichner in einem Prototyp, der den gleichen Namen hat wie der eines existierenden Makros, als ein Aufruf dieses Makros behandelt. Zum Beispiel:
#define status 23
void exit(int status);
erzeugt einen Fehler, da der Prototyp nach der Vorverarbeitung wird
void exit(int 23);
Vielleicht noch überraschender ist, was passiert, wenn der Status definiert wird
#define status []
Dann ist der resultierende Prototyp
void exit(int []);
was syntaktisch korrekt, aber semantisch ganz anders ist als die Absicht.
Um die Header-Prototypen einer Implementierung vor einer solchen Fehlinterpretation zu schützen, muss der Implementierer sie schreiben, um diese Überraschungen zu vermeiden. Mögliche Lösungen umfassen die Nichtverwendung von Bezeichnern in Prototypen oder die Verwendung von Namen im reservierten Namensraum (z __status
oder _Status
).
Siehe auch PJ Plauger Die Standard-C-Bibliothek (1992) für eine ausführliche Diskussion von Namensraumregeln und Bibliotheksimplementierungen. Das Buch bezieht sich eher auf C90 als auf eine spätere Version des Standards, aber die meisten der darin enthaltenen Implementierungshinweise sind bis heute gültig.
Namen mit doppelten führenden Unterstrichen sind für die Verwendung durch die Implementierung reserviert. Dies bedeutet nicht unbedingt, dass sie per se intern sind, obwohl sie es oft sind.
Die Idee ist, dass Sie keine Namen verwenden dürfen, die mit beginnen __
daher steht es der Implementierung frei, sie an Stellen wie Makroerweiterungen oder in den Namen von Syntaxerweiterungen (z __gcnew
ist kein Teil von C++, aber Microsoft kann es zu C++/CLI hinzufügen und sicher sein, dass kein vorhandener Code so etwas haben sollte int __gcnew;
darin würde das Kompilieren aufhören).
Um herauszufinden, was diese spezifischen Erweiterungen bedeuten, dh __const
Sie müssen die Dokumentation für Ihren spezifischen Compiler/Ihre Plattform konsultieren. In diesem speziellen Fall sollten Sie wahrscheinlich den Prototyp in der Dokumentation berücksichtigen (z http://www.kernel.org/doc/man-pages/online/pages/man3/ether_aton.3.html) als Schnittstelle der Funktion und ignorieren Sie die __const
und __THROW
Dekorationen, die in der eigentlichen Kopfzeile erscheinen.
Gemäß der Konvention in einigen Bibliotheken weist dies darauf hin, dass ein bestimmtes Symbol für den internen Gebrauch und nicht als Teil der öffentlichen API der Bibliothek vorgesehen ist.
Der Unterstrich in __const bedeutet, dass dieses Schlüsselwort eine Compiler-Erweiterung ist und seine Verwendung nicht portierbar ist (das Schlüsselwort const wurde C in einer späteren Überarbeitung hinzugefügt, 89 glaube ich). Das __THROW ist auch eine Art Erweiterung, ich nehme an, dass es für ein __attribute__ (etwas) definiert wird, wenn gcc verwendet wird, aber ich bin mir nicht sicher und zu faul, um es zu überprüfen. Die __addr kann alles bedeuten, was der Programmierer wollte, es ist nur ein Name.
Hier finden Sie eine sehr, sehr gute Zusammenfassung der Regeln für reservierte Bezeichner: stackoverflow.com/questions/228783/…
– Michael Burr
20. September 2009 um 0:46 Uhr
Technisch gesehen ist die X-Ref ein „reservierter Bezeichner für C++“, aber es gibt (jetzt) auch gute Informationen aus dem C-Standard.
– Jonathan Leffler
20. September 2009 um 1:27 Uhr