Ist “inline” ohne “static” oder “extern” in C99 jemals nützlich?

Lesezeit: 10 Minuten

Benutzeravatar von Sven Marnach
Sven Marnach

Wenn ich versuche, diesen Code zu erstellen

inline void f() {}

int main()
{
    f();
}

über die Kommandozeile

gcc -std=c99 -o a a.c

Ich erhalte einen Linker-Fehler (undefinierter Verweis auf f). Der Fehler verschwindet, wenn ich benutze static inline oder extern inline statt nur inlineoder wenn ich mit kompiliere -O (also ist die Funktion tatsächlich inline).

Dieses Verhalten scheint in Paragraph 6.7.4 (6) des C99-Standards definiert zu sein:

Wenn alle Dateibereichsdeklarationen für eine Funktion in einer Übersetzungseinheit die inline Funktionsbezeichner ohne extern, dann ist die Definition in dieser Übersetzungseinheit eine Inline-Definition. Eine Inline-Definition stellt keine externe Definition für die Funktion bereit und verbietet keine externe Definition in einer anderen Übersetzungseinheit. Eine Inline-Definition bietet eine Alternative zu einer externen Definition, die ein Übersetzer verwenden kann, um jeden Aufruf der Funktion in derselben Übersetzungseinheit zu implementieren. Es ist nicht angegeben, ob ein Aufruf der Funktion die Inline-Definition oder die externe Definition verwendet.

Wenn ich das alles richtig verstehe, eine Kompilationseinheit mit einer definierten Funktion inline wie im obigen Beispiel nur konsistent kompiliert, wenn es auch eine externe Funktion mit demselben Namen gibt, und ich nie weiß, ob meine eigene Funktion oder die externe Funktion aufgerufen wird.

Ist dieses Verhalten nicht völlig bescheuert? Ist es überhaupt sinnvoll, eine Funktion zu definieren inline ohne static oder extern im C99? Übersehe ich etwas?

Zusammenfassung der Antworten

Natürlich hat mir etwas gefehlt, und das Verhalten ist nicht dumm. 🙂

Wie Nemo erklärt, ist die Idee, das zu setzen Definition der Funktion

inline void f() {}

in der Header-Datei und nur a Erklärung

extern inline void f();

in der entsprechenden .c-Datei. Nur der extern -Deklaration löst die Generierung von extern sichtbarem Binärcode aus. Und es nützt ja nichts inline in einer .c-Datei – es ist nur in Kopfzeilen nützlich.

Wie die in Jonathans Antwort zitierte Begründung des C99-Komitees erklärt, inline dreht sich alles um Compiler-Optimierungen, die die Definition einer Funktion erfordern, die an der Stelle eines Aufrufs sichtbar sein muss. Dies kann nur erreicht werden, indem die Definition in den Header eingefügt wird, und natürlich darf eine Definition in einem Header nicht jedes Mal Code ausgeben, wenn sie vom Compiler gesehen wird. Da der Compiler jedoch nicht gezwungen ist, eine Funktion tatsächlich einzufügen, muss irgendwo eine externe Definition vorhanden sein.

  • grenzwertiges Duplikat von stackoverflow.com/questions/5369888/…

    – Earlz

    10. Juni 2011 um 22:40 Uhr

  • Und auch stackoverflow.com/questions/216510/extern-inline

    – Nemo

    10. Juni 2011 um 22:46 Uhr

  • @Earlz: Danke für den Link. Meine Frage bezieht sich auf die Begründung des zitierten Absatzes der Norm und ob es jemals Anwendungsfälle dafür gibt inline ohne static und extern, obwohl. Leider wird keines dieser Probleme in dieser Frage behandelt.

    – Sven Marnach

    10. Juni 2011 um 22:46 Uhr

  • @Nemo: Ich habe diese Frage und die Antworten gelesen, bevor ich meine gepostet habe. Auch hier frage ich nicht “wie verhält es sich?” sondern “was ist die idee hinter diesem verhalten”? Ich bin mir ziemlich sicher, dass ich hier etwas übersehe.

    – Sven Marnach

    10. Juni 2011 um 22:49 Uhr

  • ja, deshalb sagte ich “grenzwertig”. Ich persönlich denke, das stellt eine ganz andere Frage

    – Earlz

    10. Juni 2011 um 22:57 Uhr

Benutzeravatar von Nemo
Nemo

Eigentlich beantwortet diese ausgezeichnete Antwort auch Ihre Frage, denke ich:

Was macht extern inline?

Die Idee ist, dass “inline” in einer Header-Datei verwendet werden kann und dann “extern inline” in einer .c-Datei. Mit “extern inline” teilen Sie dem Compiler einfach mit, welche Objektdatei den (von außen sichtbaren) generierten Code enthalten soll.

[update, to elaborate]

Ich glaube nicht, dass “inline” (ohne “static” oder “extern”) in einer .c-Datei verwendet wird. Aber in einer Header-Datei macht es Sinn, und es erfordert eine entsprechende “extern inline”-Deklaration in einer .c-Datei, um den eigenständigen Code tatsächlich zu generieren.

  • Ja, ich habe das bis jetzt selbst nie verstanden, also danke der Nachfrage :-). Es ist insofern seltsam, als das nicht-externe “Inline” Definition geht in den Header (führt aber nicht unbedingt zu einer Codegenerierung), während das “extern inline” Erklärung geht in die .c-Datei und bewirkt tatsächlich, dass der Code generiert wird.

    – Nemo

    10. Juni 2011 um 22:57 Uhr


  • Heißt das, ich schreibe inline void f() {} in der Kopfzeile u extern inline void f(); in der .c-Datei? Die eigentliche Funktionsdefinition steht also im Header und die .c-Datei enthält in diesem Fall eine bloße Deklaration, in umgekehrter Reihenfolge?

    – Sven Marnach

    10. Juni 2011 um 22:57 Uhr

  • @endolith: Ich verstehe deine Frage nicht. Wir diskutieren inline ohne static oder extern. Na sicher static inline ist in Ordnung, aber darum geht es in dieser Frage und Antwort nicht.

    – Nemo

    22. Februar 2012 um 21:16 Uhr


  • @KunWu: Die inline f() Die Definition gibt keinen Code aus, sodass sie zur Verbindungszeit zu einem undefinierten Symbol führt. Das static Schlüsselwort bewirkt, dass die Definition (lokal sichtbaren) Code ausgibt. Dies ist die Semantik … Ein optimierender Compiler könnte den Code in beiden Fällen tatsächlich inline erweitern oder sogar ohne inline Stichwort. In C, die inline Das Schlüsselwort hat mehr mit Bereichs- und Verknüpfungsregeln zu tun als mit tatsächlichem Inlining.

    – Nemo

    8. Februar 2014 um 17:40 Uhr

  • @MatthieuMoy Sie müssen verwenden -std=c99 Anstatt von -std=gnu89.

    – a3f

    17. Februar 2017 um 6:19 Uhr


Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Aus der Norm (ISO/IEC 9899:1999) selbst:

Anhang J.2 Undefiniertes Verhalten

  • Eine Funktion mit externer Verknüpfung wird mit einem deklariert inline Funktionsbezeichner, ist aber nicht auch in derselben Übersetzungseinheit (6.7.4) definiert.

Das C99-Komitee schrieb a Begründungund es heißt:

6.7.4 Funktionsbezeichner

Eine neue Funktion von C99: Das inline Schlüsselwort, adaptiert von C++, ist a Funktionsbezeichner die nur in Funktionsdeklarationen verwendet werden kann. Es ist nützlich für Programmoptimierungen, die erfordern, dass die Definition einer Funktion an der Stelle eines Aufrufs sichtbar ist. (Beachten Sie, dass der Standard nicht versucht, die Art dieser Optimierungen anzugeben.)

Die Sichtbarkeit ist gewährleistet, wenn die Funktion eine interne Verknüpfung hat oder wenn sie eine externe Verknüpfung hat und der Aufruf in derselben Übersetzungseinheit wie die externe Definition ist. In diesen Fällen ist das Vorhandensein der
inline Das Schlüsselwort in einer Deklaration oder Definition der Funktion hat keine Auswirkung, außer darauf hinzuweisen, dass Aufrufe dieser Funktion gegenüber Aufrufen anderer Funktionen, die ohne deklariert wurden, bevorzugt optimiert werden sollten inline Stichwort.

Sichtbarkeit ist ein Problem für einen Aufruf einer Funktion mit externer Verknüpfung, wo der Aufruf in einer anderen Übersetzungseinheit als der Definition der Funktion erfolgt. In diesem Fall ist die inline Das Schlüsselwort ermöglicht, dass die Übersetzungseinheit, die den Aufruf enthält, auch eine lokale oder Inline-Definition der Funktion enthält.

Ein Programm kann eine Übersetzungseinheit mit einer externen Definition, eine Übersetzungseinheit mit einer Inline-Definition und eine Übersetzungseinheit mit einer Deklaration, aber keiner Definition für eine Funktion enthalten. Aufrufe in der letztgenannten Übersetzungseinheit verwenden wie üblich die externe Definition.

Eine Inline-Definition einer Funktion wird als eine andere Definition betrachtet als die externe Definition. Wenn ein Aufruf an eine Funktion func bei externer Verknüpfung auftritt, wo eine Inline-Definition sichtbar ist, ist das Verhalten dasselbe, als ob der Aufruf beispielsweise an eine andere Funktion erfolgen würde
__func, mit interner Verlinkung. Ein konformes Programm darf nicht davon abhängen, welche Funktion aufgerufen wird. Dies ist das Inline-Modell im Standard.

Ein konformes Programm darf sich weder auf die Implementierung unter Verwendung der Inline-Definition noch auf die Implementierung unter Verwendung der externen Definition verlassen. Die Adresse einer Funktion ist immer die Adresse, die der externen Definition entspricht, aber wenn diese Adresse zum Aufrufen der Funktion verwendet wird, könnte die Inline-Definition verwendet werden. Daher verhält sich das folgende Beispiel möglicherweise nicht wie erwartet.

inline const char *saddr(void)
{
    static const char name[] = "saddr";
    return name;
}
int compare_name(void)
{
    return saddr() == saddr(); // unspecified behavior
}

Da die Implementierung möglicherweise die Inline-Definition für einen der Aufrufe von verwendet saddr und die externe Definition für die andere verwenden, wird die Gleichheitsoperation nicht garantiert als 1 (wahr) ausgewertet. Dies zeigt, dass innerhalb der Inline-Definition definierte statische Objekte sich von ihrem entsprechenden Objekt in der externen Definition unterscheiden. Dies begründete den Zwang, sogar eine Nicht-Definition zu definieren.const Objekt dieser Art.

Inlining wurde dem Standard so hinzugefügt, dass es mit vorhandener Linker-Technologie implementiert werden kann, und eine Teilmenge von C99-Inlining ist mit C++ kompatibel. Dies wurde dadurch erreicht, dass genau eine Übersetzungseinheit, die die Definition einer Inline-Funktion enthält, als diejenige angegeben werden muss, die die externe Definition für die Funktion bereitstellt. Weil diese Spezifikation einfach aus einer Deklaration besteht, der entweder die fehlt inline Schlüsselwort oder enthält beides inline und externwird es auch von einem C++-Übersetzer akzeptiert.

Inlining in C99 erweitert die C++-Spezifikation auf zwei Arten. Erstens, wenn eine Funktion deklariert ist
inline in einer Übersetzungseinheit muss sie nicht deklariert werden inline in jeder anderen Übersetzungseinheit. Dies ermöglicht beispielsweise eine Bibliotheksfunktion, die innerhalb der Bibliothek eingebunden werden soll, aber an anderer Stelle nur über eine externe Definition verfügbar ist. Die Alternative, eine Wrapper-Funktion für die externe Funktion zu verwenden, erfordert einen zusätzlichen Namen; und es kann sich auch nachteilig auf die Leistung auswirken, wenn ein Übersetzer nicht tatsächlich eine Inline-Ersetzung vornimmt.

Zweitens wird die Anforderung, dass alle Definitionen einer Inline-Funktion „genau gleich“ sein müssen, durch die Anforderung ersetzt, dass das Verhalten des Programms nicht davon abhängen sollte, ob ein Aufruf mit einer sichtbaren Inline-Definition oder der externen Definition von a implementiert wird Funktion. Dadurch kann eine Inline-Definition für ihre Verwendung innerhalb einer bestimmten Übersetzungseinheit spezialisiert werden. Beispielsweise kann die externe Definition einer Bibliotheksfunktion einige Argumentvalidierungen enthalten, die für Aufrufe von anderen Funktionen in derselben Bibliothek nicht erforderlich sind. Diese Erweiterungen bieten einige Vorteile; und Programmierer, die sich Sorgen um die Kompatibilität machen, können sich einfach an die strengeren C++-Regeln halten.

Beachten Sie, dass dies der Fall ist nicht geeignet für Implementierungen, um Inline-Definitionen von Standardbibliotheksfunktionen in den Standardheadern bereitzustellen, da dies einigen Legacy-Code beschädigen kann, der Standardbibliotheksfunktionen nach dem Einfügen ihrer Header neu deklariert. Das inline Das Schlüsselwort soll Benutzern nur eine portable Möglichkeit bieten, das Inlining von Funktionen vorzuschlagen. Da die Standard-Header nicht portierbar sein müssen, haben Implementierungen andere Optionen wie:

#define abs(x) __builtin_abs(x)

oder andere nicht-portable Mechanismen zum Einbetten von Standardbibliotheksfunktionen.

Benutzeravatar von Fred Foo
Fred Fu

> Ich erhalte einen Linker-Fehler (undefinierter Verweis auf f)

Funktioniert hier: Linux x86-64, GCC 4.1.2. Kann ein Fehler in Ihrem Compiler sein; Ich sehe nichts in dem zitierten Absatz aus dem Standard, der das angegebene Programm verbietet. Beachten Sie die Verwendung von wenn statt iff.

Eine Inline-Definition bietet eine Alternative zu einer externen Definitiondie ein Übersetzer kann verwenden, um einen beliebigen Aufruf der Funktion in derselben Übersetzungseinheit zu implementieren.

Also, wenn Sie das Verhalten der Funktion kennen f und Sie es in einer engen Schleife aufrufen möchten, können Sie seine Definition kopieren und in ein Modul einfügen, um Funktionsaufrufe zu verhindern; oder, können Sie eine Definition angeben, die für die Zwecke des aktuellen Moduls äquivalent ist (aber die Eingabevalidierung oder andere Optimierungen, die Sie sich vorstellen können, überspringen). Der Compiler-Schreiber hat jedoch die Möglichkeit, stattdessen die Programmgröße zu optimieren.

  • Der Standard besagt, dass der Compiler immer davon ausgehen kann, dass es eine externe Funktion mit demselben Namen gibt, und sie anstelle der Inline-Version aufruft, daher glaube ich nicht, dass es sich um einen Compiler-Fehler handelt. Ich habe es mit gcc 4.3, 4.4 und 4.5 versucht, alle geben einen Linker-Fehler.

    – Sven Marnach

    10. Juni 2011 um 23:23 Uhr

1422010cookie-checkIst “inline” ohne “static” oder “extern” in C99 jemals nützlich?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy