C/C++: Statische Funktion in Header-Datei, was bedeutet das?

Lesezeit: 8 Minuten

Benutzer-Avatar
Jarod

Ich weiß, was es bedeutet, wenn eine statische Funktion in der Quelldatei deklariert wird. Ich lese Code und habe festgestellt, dass statische Funktionen in Header-Dateien in anderen Dateien aufgerufen werden können.

Benutzer-Avatar
entspannen

Ist die Funktion in der Header-Datei definiert? Damit der eigentliche Code direkt in der Funktion angegeben wird, etwa so:

static int addTwo(int x)
{
  return x + 2;
}

Dann ist das nur eine Möglichkeit, viele verschiedene C-Dateien mit einer nützlichen Funktion auszustatten. Jede C-Datei, die den Header enthält, erhält ihre eigene Definition, die sie aufrufen kann. Dies verschwendet natürlich Speicher und ist (meiner Meinung nach) eine ziemlich hässliche Sache, da es im Allgemeinen keine gute Idee ist, ausführbaren Code in einem Header zu haben.

Erinnere dich daran #include:ing a header fügt im Grunde nur den Inhalt des Headers (und aller anderen darin enthaltenen Header) in die C-Datei ein, wie sie vom Compiler gesehen werden. Der Compiler weiß nie, dass die eine bestimmte Funktionsdefinition aus einer Header-Datei stammt.

AKTUALISIEREN: In vielen Fällen ist es tatsächlich eine gute Idee, so etwas wie oben zu tun, und ich weiß, dass meine Antwort diesbezüglich sehr schwarz-weiß klingt, was die Dinge ein wenig zu sehr vereinfacht. Code zum Beispiel, der modelliert (oder nur verwendet) intrinsische Funktionen kann wie oben ausgedrückt werden, und mit einem expliziten inline Stichwort sogar:

static inline int addTwo(int *x)
{
  __add_two_superquickly(x);
}

Hier die __add_two_superquickly() function ist ein fiktives Intrinsic, und da wir möchten, dass die gesamte Funktion im Grunde zu einer einzigen Anweisung kompiliert wird, möchten wir wirklich, dass sie inline ist. Dennoch ist das Obige sauberer als die Verwendung eines Makros.

Der Vorteil gegenüber der direkten direkten Verwendung des Intrinsic liegt natürlich darin, dass es möglich ist, den Code auf Compilern zu erstellen, denen dieses bestimmte Intrinsic fehlt, indem man es in eine andere Abstraktionsebene einpackt, indem man eine alternative Implementierung bereitstellt und die richtige auswählt, je nachdem, welcher Compiler verwendet wird .

  • Nun, der Compiler wird wahrscheinlich kurze Funktionen einbetten. Es könnte also tatsächlich weniger Speicher verbrauchen, wenn die Funktion kurz genug ist. Aber ich würde ein “Inline” voranstellen, damit Sie keine Kompilierungswarnungen über nicht verwendete statische Funktionen erhalten.

    – Quinmare

    23. April 2009 um 9:26 Uhr

  • @quinmars Guter Punkt, ich habe bearbeitet. Besser spät, hoffe ich. 🙂 Vielen Dank.

    – abschalten

    2. Februar 2014 um 17:58 Uhr

  • Ich frage mich, ob der Linker das optimieren wird. Es sieht, dass addTwo auf b.obj nicht referenziert wird, dann entfernt es die Definition aus dem obj? Wenn dies der Fall ist, beträgt der Overhead nur (Größe der Funktion) * (Anzahl verschiedener obj-Dateien, die darauf verweisen). Immer noch größer als (Größe der Funktion), aber nicht so schlimm?

    – stewart99

    30. Juni 2014 um 22:10 Uhr


  • @jheriko Da jede C-Datei, die den Header enthält, eine eigene lokale Funktion erhält, wird der Code in der Funktion viele Male wiederholt. Verglichen mit einer einzigen gemeinsamen Definition, die ist namens von vielen Benutzern verschwendet das Speicher.

    – abschalten

    21. September 2016 um 11:10 Uhr

Es wird effektiv eine separate statische Funktion mit demselben Namen in jeder cpp-Datei erstellt, in der es enthalten ist. Dasselbe gilt für globale Variablen.

Wie andere sagen, hat es genau die gleiche Bedeutung wie a static Funktion in der .c Datei selbst. Dies liegt daran, dass es keinen semantischen Unterschied zwischen gibt .c und .h Dateien; es gibt nur die Übersetzungseinheit, die aus der tatsächlich an den Compiler übergebenen Datei besteht (meist mit dem Namen .c) mit dem Inhalt aller in genannten Dateien #include Linien (normalerweise benannt .h) in den Stream eingefügt, wie sie vom Präprozessor gesehen werden.

Die Konvention, dass sich die C-Quelle in einer Datei mit dem Namen befindet .c und öffentliche Erklärungen befinden sich in Dateien namens .h ist nur eine Konvention. Aber im Allgemeinen ist es gut. Unter dieser Konvention sollten die einzigen Dinge, die in erscheinen sollten .h Dateien sind Deklarationen, sodass Sie im Allgemeinen vermeiden, dasselbe Symbol zu haben definiert mehr als einmal in einem einzigen Programm.

In diesem speziellen Fall ist die static Das Schlüsselwort macht das Symbol für das Modul privat, sodass es keinen Konflikt mit mehreren Definitionen gibt, der darauf wartet, Probleme zu verursachen. In diesem einen Sinne ist es also sicher. Aber in Ermangelung einer Garantie, dass die Funktion inliniert wird, gehen Sie das Risiko ein, dass die Funktion in jedem Modul, das passiert, instanziiert wird #include diese Header-Datei, die bestenfalls eine Verschwendung von Speicher im Codesegment ist.

Ich bin mir nicht sicher, welche Anwendungsfälle dies überhaupt in einem allgemein verfügbaren öffentlichen Header rechtfertigen würden.

Wenn die .h Datei ist generierter Code und nur in einer einzigen enthalten .c Datei, dann würde ich persönlich der Datei einen anderen Namen geben als .h um zu betonen, dass es eigentlich gar kein öffentlicher Header ist. Beispielsweise könnte ein Dienstprogramm, das eine Binärdatei in eine initialisierte Variablendefinition konvertiert, eine Datei schreiben, die über verwendet werden soll #include und könnte sehr gut enthalten a static Deklaration der Variablen, und möglicherweise sogar static Definitionen von Accessor- oder anderen verwandten Hilfsfunktionen.

  • Vielleicht enthält die Funktion statische Variablen, die ihren Wert zwischen Aufrufen (interner Zustand) beibehalten, und somit erhält jedes Modul “seine eigene private Kopie” der Variablen, indem es seinen eigenen Klon der Funktion hat?

    – Nikolaus Miari

    12. Juli 2012 um 22:21 Uhr

  • @ranReloaded, das ist eine Möglichkeit. Ich persönlich würde es vermeiden, weil es einem cleveren Optimierer (oder Code-Betreuer) zu viele Chancen bieten würde, es zu brechen, indem er die scheinbar redundanten Funktionskörper “clever” eliminiert. Außerdem stellt es genau einen Satz interner Zustände pro Übersetzungseinheit bereit. Es wäre weniger verwirrend und weniger zerbrechlich, den gesamten Zustand in eine explizite Zustandsvariable zu stecken, die an jeden Aufruf übergeben wird.

    – RBerteig

    13. Juli 2012 um 23:50 Uhr

  • Ja, ich stimme diesem “Design” auch nicht zu. Ich würde lieber private Daten in dateiglobalen statischen Variablen (C) oder Klassenmitgliedern (C++) kapseln.

    – Nikolaus Miari

    14. Juli 2012 um 6:21 Uhr

  • Der Vorteil ist jedoch, dass die privaten Daten gekapselt sind und genau dort sichtbar sind, wo sie benötigt werden, und nirgendwo sonst.

    – Nikolaus Miari

    14. Juli 2012 um 8:32 Uhr

Benutzer-Avatar
Galinette

Wenn Sie die Funktion in einer Header-Datei definieren (nicht einfach deklarieren), wird eine Kopie der Funktion in jeder Übersetzungseinheit generiert (im Grunde in jeder cpp-Datei, die diesen Header enthält).

Dies kann die Größe Ihrer ausführbaren Datei erhöhen, aber dies kann vernachlässigbar sein, wenn die Funktion klein ist. Der Vorteil besteht darin, dass die meisten Compiler die Funktion inlinen können, was die Codeleistung erhöhen kann.

Aber es kann eine geben groß Unterschied dabei, der in keiner Antwort erwähnt wurde. Wenn Ihre Funktion eine statische lokale Variable verwendet, wie z.

static int counter()
{
    static int ctr = 0;
    return ctr++;
}

Eher, als:

//header
int counter();

//source
int counter()
{
    static int ctr = 0;
    return ctr++;
}

Dann hat jede Quelldatei, die diesen Header enthält, einen eigenen Zähler. Wenn die Funktion innerhalb des Headers deklariert und in einer Quelldatei definiert ist, wird der Zähler von Ihrem gesamten Programm gemeinsam genutzt.

Es ist also falsch zu sagen, dass der einzige Unterschied in der Leistung und der Codegröße besteht.

Es gibt keinen semantischen Unterschied bei der Definition in der Quelldatei oder der Header-Datei, im Grunde bedeutet beides in einfachem C dasselbe, wenn Sie das statische Schlüsselwort verwenden, dass Sie den Bereich einschränken.

Es gibt jedoch ein Problem, dies in die Header-Datei zu schreiben, da Sie jedes Mal, wenn Sie den Header in eine Quelldatei einfügen, eine Kopie der Funktion mit derselben Implementierung haben, die einer normalen im Header definierten Funktion sehr ähnlich ist Datei. Durch das Hinzufügen der Definition im Header erreichen Sie nicht das, wofür die statische Funktion gedacht ist.

Daher schlage ich vor, dass Sie Ihre Implementierung nur in Ihrer Quelldatei und nicht im Header haben sollten.

  • Es gibt einen starken semantischen Unterschied, wenn die Funktion eine statische lokale Variable enthält. In einem Fall wird die statische Variable geteilt, im anderen Fall gibt es mehrere verschiedene statische Variablen für jede Kompilationseinheit. Daher kann das Funktionsverhalten völlig unterschiedlich sein.

    – Galette

    5. April 2017 um 8:10 Uhr

Benutzer-Avatar
kay

Es ist nützlich in einigen “nur-Header”-Bibliotheken mit kleinen Inline-Funktionen. In einem solchen Fall möchten Sie immer eine Kopie der Funktion erstellen, damit dies kein schlechtes Muster ist. Dies gibt Ihnen jedoch eine einfache Möglichkeit, separate Schnittstellen- und Implementierungsteile in die einzelne Header-Datei einzufügen:

// header.h

// interface part (for user?!)
static inline float av(float a, float b);

// implementation part (for developer)
static inline float av(float a, float b)
{
    return (a+b)/2.f;
}

Die Apple Vector Math Library im GLK-Framework verwendet eine solche Konstruktion (z. B. GLKMatrix4.h).

  • Es gibt einen starken semantischen Unterschied, wenn die Funktion eine statische lokale Variable enthält. In einem Fall wird die statische Variable geteilt, im anderen Fall gibt es mehrere verschiedene statische Variablen für jede Kompilationseinheit. Daher kann das Funktionsverhalten völlig unterschiedlich sein.

    – Galette

    5. April 2017 um 8:10 Uhr

1012840cookie-checkC/C++: Statische Funktion in Header-Datei, was bedeutet das?

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

Privacy policy