Warum eine C-Funktion als statisch inline deklarieren?

Lesezeit: 5 Minuten

Ich bin auf ein Beispiel für eine C-Funktion gestoßen, die wie folgt deklariert ist:

static inline CGPoint SOCGPointAdd(const CGPoint a, const CGPoint b) {
    return CGPointMake(a.x + b.x, a.y + b.y);
}

Bisher habe ich Dienstprogramm-C-Funktionen in .h-Dateien deklariert und sie in .m-Dateien implementiert, einfach so:

CGPoint SOCGPointAdd(const CGPoint a, const CGPoint b) {
    return CGPointMake(a.x + b.x, a.y + b.y);
}

Ich kann diese Funktion “inline” verwenden, wo immer ich will, und sie sollte auch “statisch” sein, da sie keinem Objekt zugeordnet ist, wie eine Objective-c-Methode. Was ist der Sinn / Vorteil der Angabe von “statisch” und “inline”?

  • Dieser Beitrag könnte Ihre Frage beantworten stackoverflow.com/questions/145838/…

    – Benutzer376507

    17. Februar 2014 um 17:38 Uhr

  • Schauen Sie sich diese Antwort (auf eine etwas andere Frage) an stackoverflow.com/a/7767858/464988

    – MByD

    17. Februar 2014 um 17:40 Uhr

Benutzer-Avatar
Eric Postpischil

inline bedeutet nicht, dass Sie die Funktion „inline“ verwenden können (es ist normal, Funktionen innerhalb anderer Funktionen zu verwenden; Sie brauchen es nicht inline dafür); Es ermutigt den Compiler, die Funktion dort in den Code einzubauen, wo sie verwendet wird (im Allgemeinen mit dem Ziel, die Ausführungsgeschwindigkeit zu verbessern).

static bedeutet, dass der Funktionsname nicht extern verknüpft ist. Wenn die Funktion nicht deklariert wurde static, wird der Compiler benötigt, um es nach außen sichtbar zu machen, damit es mit anderen Objektmodulen verknüpft werden kann. Dazu muss der Compiler eine separate Nicht-Inline-Instanz der Funktion einschließen. Durch Deklaration der Funktion staticerlauben Sie, dass alle Instanzen davon in das aktuelle Modul eingebettet werden, und hinterlassen möglicherweise keine separate Instanz.

static inline wird normalerweise mit kleinen Funktionen verwendet, die besser in der aufrufenden Routine ausgeführt werden als durch Verwendung eines Aufrufmechanismus, einfach weil sie so kurz und schnell sind, dass es besser ist, sie tatsächlich auszuführen, als eine separate Kopie aufzurufen. Z.B:

static inline double square(double x) { return x*x; }

  • EIN Modul ist eine Kompilationseinheit: eine Quelldatei plus alle darin enthaltenen Header-Dateien. Wenn Sie mehrere Module separat zusammenstellen, müssen diese zu einem Programm verknüpft werden. Der Weg static verwendet wird, kommt teilweise aus der Geschichte der Sprache. Wenn wir es heute von Grund auf neu definieren würden, könnten wir dies als Verwendung davon bezeichnen internal stattdessen. Das betreffende Konzept heißt Verknüpfung. Bezeichner in C können drei Arten von Verknüpfungen haben: extern, intern und keine. Ein Bezeichner mit externer Verknüpfung kann in mehreren Modulen verwendet werden, und da sie verknüpft sind, verweist der Bezeichner auf …

    – Eric Postpischil

    17. Februar 2014 um 18:03 Uhr

  • … zum selben Objekt in verschiedenen Modulen. Ein Bezeichner mit interner Verknüpfung bezieht sich auf ein Objekt nur innerhalb eines Moduls. Wenn Sie denselben Bezeichner in einem anderen Modul verwenden, verweist er auf ein anderes Objekt; Sie sind nicht verknüpft, um auf dasselbe Objekt zu verweisen. (Übrigens, was ich hier ein Modul nenne, ist das, was der C-Standard als a bezeichnet Übersetzungseinheit.) Ein Bezeichner mit interner Verknüpfung kann in mehreren Funktionen innerhalb eines Moduls verwendet werden, um auf dasselbe Objekt zu verweisen. Dann gibt es Kennungen ohne Verknüpfung. Sie beziehen sich auf ein Objekt nur innerhalb des Blocks, in dem sie deklariert sind.

    – Eric Postpischil

    17. Februar 2014 um 18:05 Uhr

  • Die Regeln für die Verknüpfungskennungen sind aufgrund der Geschichte etwas kompliziert. Grob gesagt haben im Dateibereich deklarierte Bezeichner standardmäßig eine externe Verknüpfung. Hinzufügen static ändert sie in interne Verknüpfungen. Bezeichner, die im Blockbereich (innerhalb von Funktionen) deklariert werden, haben standardmäßig keine Verknüpfung. Hinzufügen static ändert sie auch in interne Verknüpfungen. Hier ist eine Komplikation: Hinzufügen extern ändert stattdessen eine Deklaration im Blockbereich so, dass sie auf eine zuvor sichtbare Deklaration verweist, anstatt keine Verknüpfung zu haben. Diese zuvor sichtbare Erklärung könnte haben staticergebend…

    – Eric Postpischil

    17. Februar 2014 um 18:07 Uhr

  • … eine Erklärung markiert extern Verweis auf eine Kennung mit interner Verknüpfung. Obendrein, static ändert nicht nur die Verknüpfung von in Blöcken deklarierten Bezeichnern, sondern auch die Speicherklasse ihrer Objekte (wenn die Objekte erstellt und zerstört werden).

    – Eric Postpischil

    17. Februar 2014 um 18:09 Uhr

  • Korrektur: Bezeichner deklariert mit static im Blockbereich haben immer noch keine Verknüpfung, keine interne Verknüpfung. Nur im Dateibereich static interne Verknüpfung vermitteln.

    – Eric Postpischil

    17. Februar 2014 um 18:56 Uhr

Benutzer-Avatar
Merlevede

Wenn die Speicherklasse extern ist, hat der Bezeichner eine externe Verknüpfung und die Inline-Definition stellt auch die externe Definition bereit. Wenn die Speicherklasse ist statischder Bezeichner hat eine interne Verknüpfung und die Inline-Definition ist in anderen Übersetzungseinheiten unsichtbar.

Durch die Deklaration einer Funktion in der Reihe, können Sie den Compiler anweisen, den Code dieser Funktion in den Code für seine Aufrufer zu integrieren (um den vollständigen Code dieser Funktion direkt an der Stelle zu ersetzen, von der aus sie aufgerufen wurde). Dadurch wird die Ausführung beschleunigt, indem der Funktionsaufruf-Overhead eliminiert wird. Deshalb sollten Inline-Funktionen sehr kurz sein.

  • Ich bin ein bisschen eingerostet in meinem Obj-C, aber ich dachte, dass eine C-Statik nicht viel mit Obj-C-Klassen und statischen (Klassen-) Methoden zu tun hat, zumal dies eine Funktion und keine Methode ist?

    – Jo

    17. Februar 2014 um 17:40 Uhr


Benutzer-Avatar
Lewis Kelsey

In C, inline bedeutet, dass es sich um eine Inline-Definition handelt. Es hat keine interne Verknüpfung, es hat nein Verknüpfung. Es erreicht nie den Linker, was bedeutet, dass, wenn der Compiler diese Inline-Definition nicht verwendet, um jeden einzelnen Verweis auf die Funktion in der Kompilierungseinheit einzufügen, ein lokaler Linker-Fehler auftritt, wenn ein Symbol mit demselben Namen (C verwendet unverfälschte Identifikatoren) mit externer Verknüpfung wird nicht von einer anderen Übersetzungseinheit in der Zusammenstellung exportiert. Das eigentliche Inlining von Verweisen auf die Funktion durch den Compiler wird ausschließlich durch das Optimierungs-Flag oder gesteuert __attribute__((always_inline))

Es gibt keinen Unterschied zwischen static inline und staticinline beide die Funktion nicht und stellen die Funktion in der Assemblyausgabe auf -O0 als internes Verknüpfungssymbol für den Linker bereit, und beide inlinen und optimieren die Einbeziehung der Funktion in die Assemblyausgabe auf -O1. static inline hat eine Besonderheit, da Sie einen nicht statischen Inline-Prototyp davor verwenden können, außer dass dieser Prototyp ignoriert und nicht als Vorwärtsdeklaration verwendet wird (aber die Verwendung eines nicht statischen Prototyps vor einer statischen Funktion ist ein Fehler).

  • inline (GCC <5.0, die verwendet -std=gnu90 / gnu89 als Standard) / extern inline (GCC 5.0 und höher, das verwendet -std=gnu11): Dies ist eine Inline-Definition nur für den Compiler. Eine extern sichtbare Funktionsemission (in der Assembly-Ausgabe zur Verwendung des Assemblers und Linkers) für diese Inline-Definition tritt nicht auf. Wenn nicht alle Verweise auf die Funktion in der Datei tatsächlich vom Compiler eingefügt werden (und das Inlining auf höheren Optimierungsstufen erfolgt oder wenn Sie __attribute__((always_inline)) inline float func()), dann kommt es zu einem lokalen Linkerfehler, wenn der Compiler die externe Definition nicht an den Linker ausgibt (und wenn ein gleichnamiges Symbol mit externer Verknüpfung nicht von einer anderen Übersetzungseinheit exportiert wird). Dadurch können eine Inline-Definition und eine Out-of-Line-Funktion desselben Symbols separat definiert werden, eine mit Inline und die andere Out-of-Line, aber nicht in derselben Übersetzungseinheit, da der Compiler sie verwechseln würde, und eine Definition außerhalb der Linie wird als Neudefinitionsfehler behandelt. Inline-Definitionen sind immer nur für den Compiler sichtbar und jede Übersetzungseinheit kann ihre eigene haben. Inline-Definitionen können nicht in andere Dateien exportiert werden, da Inline-Definitionen die Verknüpfungsphase nicht erreichen. Um dies zur Kompilierzeit zu erreichen, kann sich die Inline-Definition in einer Header-Datei befinden und in jeder Übersetzungseinheit enthalten sein. Das bedeutet, dass die Verwendung von inline eine Compiler-Anweisung ist und extern/static sich auf die für den Linker erzeugte Out-of-Line-Version bezieht. Wenn die Funktion nicht in der Übersetzungseinheit definiert ist, kann sie nicht eingebettet werden, da sie dem Linker überlassen wird. Wenn die Funktion definiert, aber nicht inline ist, verwendet der Compiler diese Version, wenn er sich für Inline entscheidet

  • extern inline (GCK <5.0) / inline (GCC >5.0): Für diese Inline-Definition wird eine extern sichtbare Funktion ausgegeben, unabhängig davon, ob sie Inline ist oder nicht, was bedeutet, dass dieser Bezeichner nur in einer der Übersetzungseinheiten verwendet werden kann. Dies ist intuitiv das Gegenteil von ‘extern’

  • static inline: Die lokal sichtbare Out-of-Line-Funktion wird vom Compiler mit einer lokalen Direktive für den Assembler für diese Compiler-Inline-Definition an die Assembly-Ausgabe ausgegeben, kann aber auf höheren Optimierungsstufen optimiert werden, wenn alle Funktionen inline eingebunden werden können; es wird niemals zulassen, dass ein Linker-Fehler entsteht. Es verhält sich identisch zu static weil der Compiler die static Definition auf höheren Optimierungsstufen genauso wie static inline.

  • Ein inline Funktion, die es nicht ist static keine nicht konstanten Variablen für die statische Speicherdauer enthalten oder auf statische Dateibereichsvariablen zugreifen sollten, wird dies eine Compiler-Warnung erzeugen. Dies liegt daran, dass die Inline- und Out-of-Line-Versionen der Funktion unterschiedliche statische Variablen haben, wenn die Out-of-Line-Version von einer anderen Übersetzungseinheit bereitgestellt wird. Der Compiler kann einige Funktionen einbetten, kein lokales Symbol ausgeben, das mit diesen Referenzen verknüpft werden soll, und die Verknüpfung dem Linker überlassen, der möglicherweise ein externes Funktionssymbol findet, von dem angenommen wird, dass es dieselbe Funktion ist, da es denselben Bezeichner hat. Es erinnert den Programmierer also daran, dass es logischerweise const sein sollte, da das Ändern und Lesen der Statik zu einem undefinierten Verhalten führt; Wenn der Compiler diese Funktionsreferenz einbettet, liest er einen neuen statischen Wert in der Funktion und nicht den, auf den in einem vorherigen Aufruf der Funktion geschrieben wurde, wobei diese vorherige Referenz auf die Funktion eine war, die nicht eingebettet war, daher die Variable die im vorherigen Aufruf geschrieben wurde, wäre eine von einer anderen Übersetzungseinheit bereitgestellt worden. In diesem Fall führt dies zu einer lokalen Kopie für jede Übersetzungseinheit und einer globalen Kopie, und es ist undefiniert, auf welche Kopie zugegriffen wird. Wenn Sie es konstant machen, wird sichergestellt, dass alle Kopien identisch sind und sich nie in Bezug zueinander ändern, wodurch das Verhalten definiert und bekannt wird.

  • Mit einem inline / extern inline Prototyp vor/nach einer Nicht-Inline-Definition bedeutet, dass der Prototyp ignoriert wird.

  • Mit einem inline Prototyp vor einer Inline-Definition ist, wie man eine Inline-Funktion ohne Nebeneffekte prototypisiert, wobei das Deklarieren eines Inline-Prototyps nach der Inline-Definition nichts ändert, es sei denn, der Speicherspezifizierer ändert sich.

  • Mit einem extern inline / extern / Regulärer Prototyp vor/nach einer Inline-Definition ist identisch mit einer extern inline Definition; Es ist ein Hinweis, der eine externe Out-of-Line-Definition der Funktion unter Verwendung der Inline-Definition bereitstellt.

  • Verwenden extern inline / inline auf einen Prototyp ohne Definition in der Datei, aber es wird in den Dateiergebnissen darauf verwiesen inline ignoriert und verhält sich dann wie ein regulärer Prototyp (extern / normal, die identisch sind)

  • Verwendung einer static inline / static bei einem Prototyp ohne Definition in der Datei, aber es wird in der Datei darauf verwiesen, führt zu korrekter Verknüpfung und korrekter Typverwendung, aber eine Compiler-Warnung besagt, dass die Funktion mit interner Verknüpfung nicht definiert wurde (also verwendet sie eine externe Definition).

  • Mit einem normalen / extern / extern inline Prototyp vor a static inline oder static definition ist ein ‘statische Deklaration von ‘func’ folgt nicht-statischer Deklaration’ Fehler; Wenn Sie es danach verwenden, geschieht nichts und sie werden ignoriert. Verwendung einer static oder static inline Prototyp vor/nach a static inline Definition erlaubt. Mit einem inline Prototyp vor a static inline Die Definition wird ignoriert und fungiert nicht als Vorwärtsdeklaration. Nur so ist es möglich static inline unterscheidet sich von static als regulärer Prototyp vor a static Definition führt zu einem Fehler, dies jedoch nicht.

  • Verwendung einer static inline Prototyp vor einem regulären / extern / static / static inline / extern inline Definition ergibt sich static inline überschreibt die Bezeichner und verhält sich so korrekt wie eine Vorwärtsdeklaration.

  • __attribute__((always_inline)) fügt das Funktionssymbol immer in die Übersetzungseinheit ein und verwendet diese Definition. Das Attribut kann nur für Definitionen verwendet werden. Die Speicher-/Inline-Spezifizierer sind davon nicht betroffen und können damit verwendet werden.

  • extern inline (GCC 5.0 und höher, das verwendet -std=gnu11)” Ist das jetzt eine GNU11-Änderung oder eine C11-Änderung?

    – dyp

    15. Februar 2021 um 17:04 Uhr

  • @dyp Anscheinend tritt diese Änderung sowohl in c99 als auch in gnu99 auf

    – Lewis Kelsey

    21. Februar 2021 um 10:43 Uhr

Inline-Funktionen dienen zum Definieren in Header-Dateien. Kleine Funktionen werden in Header-Dateien definiert. Es sollte statisch sein, damit es nur auf statische Member zugreifen kann.

1365670cookie-checkWarum eine C-Funktion als statisch inline deklarieren?

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

Privacy policy