Was ist eine "statische" Funktion in C?


506

Die Frage war einfach Funktionen nicht static Methoden, wie in Kommentaren erläutert.

Ich verstehe, was eine staticVariable ist, aber was ist eine staticFunktion?

Und warum ist es so, dass, wenn ich eine Funktion deklariere, sagen void print_matrixwir, sagen wir a.c(OHNE a.h) und einschließen "a.c"- ich verstehe "print_matrix@@....) already defined in a.obj", ABER wenn ich sie deklariere static void print_matrix, kompiliert sie dann?

UPDATE Nur um die Dinge zu klären - ich weiß, dass das Einschließen .cschlecht ist, wie viele von Ihnen betonten. Ich mache es nur, um vorübergehend Speicherplatz freizugeben, main.cbis ich eine bessere Vorstellung davon habe, wie all diese Funktionen in Eigen- .hund .cDateien gruppiert werden können. Nur eine vorübergehende, schnelle Lösung.

Antworten:


685

staticFunktionen sind Funktionen, die nur für andere Funktionen in derselben Datei sichtbar sind (genauer gesagt für dieselbe Übersetzungseinheit ).

EDIT : Für diejenigen, die dachten, dass der Autor der Fragen eine 'Klassenmethode' bedeutet: Wenn die Frage markiert ist C, bedeutet er eine einfache alte C-Funktion. Für (C ++ / Java / ...) Klassenmethoden staticbedeutet dies , dass diese Methode für die Klasse selbst aufgerufen werden kann, ohne dass eine Instanz dieser Klasse erforderlich ist.


2
Eigentlich habe ich es nicht mit C ++ markiert, einige Administratoren haben es wahrscheinlich getan, aber es ging um C ++. Was ist also der Unterschied in C ++?
Slava V

16
C ++ - Methoden werden oft als "Mitgliedsfunktionen" bezeichnet, daher stimme ich zu, dass C ++ ein wenig Mehrdeutigkeit einführt. Es ist nicht deine Schuld - die Sprache verwendet das Schlüsselwort nur für zwei verschiedene Dinge.
Chuck

2
Nein, er meint immer noch eine C ++ - Funktion. Eine C ++ - freie Funktion anstelle einer C ++ - Mitgliedsfunktion.
Leichtigkeitsrennen im Orbit

3
@Chuck: In der C ++ - Terminologie wird niemals das Wort "Methode" verwendet. Das ist Java-Terminologie - in C ++ - Standarddokumenten wird sie immer als "Elementfunktion" bezeichnet (siehe diese Antwort oder dieses Glossar von C ++ - und Java-Begriffen (z. B. verwendet C ++ "Datenelement" und Java "Feld" usw.)).
ShreevatsaR

6
Um diese Antwort ein wenig zu verdeutlichen: Der Name der Funktion ist nur für andere Teile derselben Übersetzungseinheit unterhalb der ersten Deklaration dieses Namens sichtbar. Die Funktion kann von anderen Einheiten (und früheren Teilen derselben Einheit) über andere Mittel, z. B. einen Funktionszeiger, aufgerufen werden.
MM

199

Es gibt einen großen Unterschied zwischen statischen Funktionen in C und statischen Elementfunktionen in C ++. In C ist eine statische Funktion außerhalb ihrer Übersetzungseinheit, der Objektdatei, in die sie kompiliert wird, nicht sichtbar. Mit anderen Worten, wenn eine Funktion statisch gemacht wird, wird ihr Umfang eingeschränkt. Sie können sich eine statische Funktion als "privat" für ihre * .c-Datei vorstellen (obwohl dies nicht unbedingt korrekt ist).

In C ++ kann "statisch" auch für Elementfunktionen und Datenelemente von Klassen gelten. Ein statisches Datenelement wird auch als "Klassenvariable" bezeichnet, während ein nicht statisches Datenelement eine "Instanzvariable" ist. Dies ist die Smalltalk-Terminologie. Dies bedeutet, dass nur eine Kopie eines statischen Datenelements von allen Objekten einer Klasse gemeinsam genutzt wird, während jedes Objekt über eine eigene Kopie eines nicht statischen Datenelements verfügt. Ein statisches Datenelement ist also im Wesentlichen eine globale Variable, dh ein Mitglied einer Klasse.

Nicht statische Elementfunktionen können auf alle Datenelemente der Klasse zugreifen: statisch und nicht statisch. Statische Elementfunktionen können nur für statische Datenelemente ausgeführt werden.

Eine Möglichkeit, darüber nachzudenken, besteht darin, dass in C ++ statische Datenelemente und statische Elementfunktionen nicht zu einem Objekt gehören, sondern zur gesamten Klasse.


42
C ++ ist auch dateistatisch. Es ist nicht nötig, C dazu zu bringen.
Leichtigkeitsrennen im Orbit

17
In C ++ ist eine statische Funktion eine statische Funktion. Eine statische Elementfunktion ist eine statische Elementfunktion, die auch als Methode bezeichnet wird. Die Tatsache, dass C keine Mitglieder hat, bedeutet nicht, dass Funktionen "C" sind.
Gerasimos R

3
Gibt es einen Unterschied zwischen globalem var und klassischem statischem var (außer Namespace)?
Alexander Malakhov

3
Der Namespace ist der Hauptunterschied. Der andere Unterschied besteht darin, dass Sie ein statisches Datenelement privat machen und somit nur über die Elementfunktionen der Klasse zugänglich machen können. Mit anderen Worten, Sie haben viel mehr Kontrolle über ein statisches Datenelement als über eine globale Variable.
Dima

2
Könnte jemand erklären, warum das Denken an eine statische Funktion als privat für ihre .c-Datei nicht streng korrekt ist? Was bleibt noch zu sagen?
YoTengoUnLCD

77

Es gibt zwei Verwendungszwecke für das Schlüsselwort static, wenn es um Funktionen in C ++ geht.

Die erste besteht darin, die Funktion als intern verknüpft zu markieren, damit sie in anderen Übersetzungseinheiten nicht referenziert werden kann. Diese Verwendung ist in C ++ veraltet. Für diese Verwendung werden unbenannte Namespaces bevorzugt.

// inside some .cpp file:

static void foo();    // old "C" way of having internal linkage

// C++ way:
namespace
{
   void this_function_has_internal_linkage()
   {
      // ...
   }
}

Die zweite Verwendung erfolgt im Kontext einer Klasse. Wenn eine Klasse eine statische Elementfunktion hat, bedeutet dies, dass die Funktion ein Mitglied der Klasse ist (und den üblichen Zugriff auf andere Mitglieder hat), aber nicht über ein bestimmtes Objekt aufgerufen werden muss. Mit anderen Worten, innerhalb dieser Funktion gibt es keinen "this" -Zeiger.


1
Die Frage ist über statische in c.
Deqing

8
@Deqing die Frage wurde ursprünglich mit C ++ markiert und der Autor spricht über die Verwendung von ".cpp" -Dateien.
Brian Neal

57

Beispiel für einen minimalen ausführbaren Bereich mit mehreren Dateien

Hier zeige ich, wie staticsich der Umfang der Funktionsdefinitionen auf mehrere Dateien auswirkt.

ac

#include <stdio.h>

/* Undefined behavior: already defined in main.
 * Binutils 2.24 gives an error and refuses to link.
 * /programming/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
 */
/*void f() { puts("a f"); }*/

/* OK: only declared, not defined. Will use the one in main. */
void f(void);

/* OK: only visible to this file. */
static void sf() { puts("a sf"); }

void a() {
    f();
    sf();
}

Haupt c

#include <stdio.h>

void a(void);        

void f() { puts("main f"); }

static void sf() { puts("main sf"); }

void m() {
    f();
    sf();
}

int main() {
    m();
    a();
    return 0;
}

GitHub stromaufwärts .

Kompilieren und ausführen:

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o
./main

Ausgabe:

main f
main sf
main f
a sf

Deutung

  • Es gibt zwei separate Funktionen sf, eine für jede Datei
  • Es gibt eine einzige gemeinsame Funktion f

Wie üblich, je kleiner der Bereich, desto besser. Deklarieren staticSie daher immer Funktionen, wenn Sie können.

In der C-Programmierung werden Dateien häufig verwendet, um "Klassen" staticdarzustellen , und Funktionen repräsentieren "private" Methoden der Klasse.

Ein übliches C-Muster besteht darin, eine thisStruktur als erstes "Methoden" -Argument weiterzugeben , was C ++ im Grunde genommen unter der Haube tut.

Was Standards dazu sagen

C99 N1256 Entwurf 6.7.1 "Speicherklassenspezifizierer" besagt, dass dies staticein "Speicherklassenspezifizierer" ist.

6.2.2 / 3 "Verknüpfungen von Kennungen" staticimpliziert internal linkage:

Wenn die Deklaration eines Dateibereichsbezeichners für ein Objekt oder eine Funktion den statischen Speicherklassenspezifizierer enthält, ist der Bezeichner intern verknüpft.

und 6.2.2 / 2 sagt, dass internal linkagesich das wie in unserem Beispiel verhält:

In dem Satz von Übersetzungseinheiten und Bibliotheken, die ein gesamtes Programm bilden, bezeichnet jede Deklaration eines bestimmten Bezeichners mit externer Verknüpfung dasselbe Objekt oder dieselbe Funktion. Innerhalb einer Übersetzungseinheit bezeichnet jede Deklaration eines Bezeichners mit interner Verknüpfung dasselbe Objekt oder dieselbe Funktion.

Dabei ist "Übersetzungseinheit" eine Quelldatei nach der Vorverarbeitung.

Wie implementiert GCC es für ELF (Linux)?

Mit der STB_LOCALBindung.

Wenn wir kompilieren:

int f() { return 0; }
static int sf() { return 0; }

und zerlegen Sie die Symboltabelle mit:

readelf -s main.o

Die Ausgabe enthält:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 000000000000000b    11 FUNC    LOCAL  DEFAULT    1 sf
  9: 0000000000000000    11 FUNC    GLOBAL DEFAULT    1 f

Die Bindung ist also der einzige signifikante Unterschied zwischen ihnen. Valueist nur ihr Versatz in den .bssAbschnitt, also erwarten wir, dass es anders ist.

STB_LOCAList in der ELF-Spezifikation unter http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html dokumentiert :

STB_LOCAL Lokale Symbole sind außerhalb der Objektdatei mit ihrer Definition nicht sichtbar. Lokale Symbole mit demselben Namen können in mehreren Dateien vorhanden sein, ohne sich gegenseitig zu stören

Das macht es zu einer perfekten Wahl für die Darstellung static.

Funktionen ohne statische Aufladung sind STB_GLOBALund die Spezifikation sagt:

Wenn der Link-Editor mehrere verschiebbare Objektdateien kombiniert, sind nicht mehrere Definitionen von STB_GLOBAL-Symbolen mit demselben Namen zulässig.

Dies stimmt mit den Verknüpfungsfehlern bei mehreren nicht statischen Definitionen überein.

Wenn wir die Optimierung mit ankurbeln -O3, wird das sfSymbol vollständig aus der Symboltabelle entfernt: Es kann sowieso nicht von außen verwendet werden. TODO warum statische Funktionen überhaupt in der Symboltabelle behalten, wenn keine Optimierung erfolgt? Können sie für irgendetwas verwendet werden?

Siehe auch

Anonyme C ++ - Namespaces

In C ++ möchten Sie möglicherweise anonyme Namespaces anstelle von statischen verwenden, wodurch ein ähnlicher Effekt erzielt wird. Die Typdefinitionen werden jedoch weiter ausgeblendet: Unbenannte / anonyme Namespaces im Vergleich zu statischen Funktionen


3
Hinweis: void f() { puts("sf"); }(dh zwei Definitionen von f()) verursacht undefiniertes Verhalten, ohne dass eine Diagnose erforderlich ist. Es ist ein Problem mit der Qualität des Linkers, wenn tatsächlich eine Fehlermeldung angezeigt wird.
MM

2
Dies ist die beste und genaueste Erklärung! Als du!
Aqua

20

Im Folgenden geht es um einfache C-Funktionen. In einer C ++ - Klasse hat der Modifikator 'static' eine andere Bedeutung.

Wenn Sie nur eine Datei haben, macht dieser Modifikator absolut keinen Unterschied. Der Unterschied besteht in größeren Projekten mit mehreren Dateien:

In C wird jedes "Modul" (eine Kombination aus sample.c und sample.h) unabhängig kompiliert und anschließend wird jede dieser kompilierten Objektdateien (sample.o) vom Linker mit einer ausführbaren Datei verknüpft.

Angenommen, Sie haben mehrere Dateien, die Sie in Ihre Hauptdatei aufnehmen, und zwei davon haben eine Funktion, die nur intern verwendet wird. add(int a, b)Der Compiler würde leicht Objektdateien für diese beiden Module erstellen, aber der Linker gibt einen Fehler aus, weil Es findet zwei Funktionen mit demselben Namen und weiß nicht, welche es verwenden soll (auch wenn nichts zu verknüpfen ist, da sie nicht an einer anderen Stelle als in einer eigenen Datei verwendet werden).

Aus diesem Grund machen Sie diese Funktion, die nur intern verwendet wird, zu einer statischen Funktion. In diesem Fall erstellt der Compiler nicht das typische Flag "Sie können dieses Ding verknüpfen" für den Linker, sodass der Linker diese Funktion nicht sieht und keinen Fehler generiert.


16

Erstens: Es ist im Allgemeinen eine schlechte Idee, eine .cppDatei in eine andere Datei aufzunehmen - dies führt zu folgenden Problemen :-) Der normale Weg besteht darin, separate Kompilierungseinheiten zu erstellen und eine Header-Datei für die enthaltene Datei hinzuzufügen.

Zweitens:

C ++ hat hier eine verwirrende Terminologie - ich wusste nichts davon, bis in Kommentaren darauf hingewiesen wurde.

a) static functions- von C geerbt und wovon Sie hier sprechen. Außerhalb jeder Klasse. Eine statische Funktion bedeutet, dass sie außerhalb der aktuellen Kompilierungseinheit nicht sichtbar ist. In Ihrem Fall verfügt a.obj also über eine Kopie und Ihr anderer Code über eine unabhängige Kopie. (Aufblähen der endgültigen ausführbaren Datei mit mehreren Kopien des Codes).

b) static member function- was für Objektorientierung Begriffe eine statische Methode . Lebt in einer Klasse. Sie rufen dies mit der Klasse und nicht über eine Objektinstanz auf.

Diese beiden unterschiedlichen statischen Funktionsdefinitionen sind völlig unterschiedlich. Sei vorsichtig - hier sind Drachen.


Nun, ich mache es nur, um vorübergehend Speicherplatz in main.cpp freizugeben, bis ich entscheide, wie die Datei zusammen mit den richtigen .hpps in Bibliotheken organisiert werden soll. Gibt es eine bessere Idee, wie das geht?
Slava V

1
Die korrekte Terminologie in C ++ ist die Elementfunktion, nicht die Methode. In C ++ legalese gibt es keine "Methoden". Methode ist ein allgemeiner OO-Begriff. C ++ implementiert sie über Mitgliedsfunktionen.
Brian Neal

14

Statische Funktionsdefinitionen markieren dieses Symbol als intern. Es ist also nicht für Links von außen sichtbar, sondern nur für Funktionen in derselben Kompilierungseinheit, normalerweise in derselben Datei.


7

Eine statische Funktion kann im Gegensatz zu einer Instanz der Klasse für die Klasse selbst aufgerufen werden.

Zum Beispiel wäre eine nicht statische:

Person* tom = new Person();
tom->setName("Tom");

Diese Methode funktioniert für eine Instanz der Klasse, nicht für die Klasse selbst. Sie können jedoch eine statische Methode verwenden, die ohne Instanz funktioniert. Dies wird manchmal im Factory-Muster verwendet:

Person* tom = Person::createNewPerson();

2
Es scheint mir, dass Sie über statische "Methode" sprechen, nicht über "Funktion"?
Slava V

Ich nahm an, Sie beziehen sich auf statische Funktionen innerhalb einer Klasse.
Papageien

Wenn ich gewusst hätte, dass "Methoden" in C ++ "Methodenfunktionen" genannt werden, wäre mir das klarer. Nun, jetzt schon :) Danke trotzdem
Slava V

5
In C ++ gibt es keine "Methoden", nur Funktionen. Der C ++ - Standard erwähnt niemals "Methoden", sondern nur "Funktionen".
Brian Neal

1
@Puddle Ich weiß, was Sie sagen, aber im C ++ - Standard gibt es keine Definition einer "Methode". C ++ hat nur Funktionen verschiedener Art. "Methode" ist ein allgemeiner OO-Begriff und wird in anderen Sprachen und informell in C ++ verwendet. Eine Methode wird in C ++ formal als "Elementfunktion" bezeichnet.
Brian Neal

7

Minor nit: Statische Funktionen sind für eine Übersetzungseinheit sichtbar. In den meisten praktischen Fällen handelt es sich dabei um die Datei, in der die Funktion definiert ist. Der Fehler, den Sie erhalten, wird häufig als Verstoß gegen die One Definition Rule bezeichnet.

Der Standard sagt wahrscheinlich etwas wie:

"Jedes Programm muss genau eine Definition jeder nichtinline Funktion oder jedes Objekt enthalten, die in diesem Programm verwendet werden. Es ist keine Diagnose erforderlich."

Das ist die C-Sichtweise auf statische Funktionen. Dies ist in C ++ jedoch veraltet.

In C ++ können Sie außerdem Elementfunktionen als statisch deklarieren. Dies sind meistens Metafunktionen, dh sie beschreiben / modifizieren nicht das Verhalten / den Zustand eines bestimmten Objekts, sondern wirken auf die gesamte Klasse selbst. Dies bedeutet auch, dass Sie kein Objekt erstellen müssen, um eine statische Elementfunktion aufzurufen. Dies bedeutet auch, dass Sie nur innerhalb einer solchen Funktion Zugriff auf statische Elementvariablen erhalten.

Ich würde zu Parrots Beispiel das Singleton-Muster hinzufügen, das auf dieser Art einer statischen Elementfunktion basiert, um ein einzelnes Objekt während der gesamten Lebensdauer eines Programms abzurufen / zu verwenden.


7

Die Antwort auf die statische Funktion hängt von der Sprache ab:

1) In Sprachen ohne OOPS wie C bedeutet dies, dass auf die Funktion nur innerhalb der Datei zugegriffen werden kann, in der sie definiert ist.

2) In Sprachen mit OOPS wie C ++ bedeutet dies, dass die Funktion direkt in der Klasse aufgerufen werden kann, ohne eine Instanz davon zu erstellen.


Das ist nicht wahr. Die Erklärung Ihres zweiten Absatzes bezieht sich auf " statische Elementfunktionen " einer Klasse, nicht auf " statische Funktionen ". In C ++ hat eine mit qualifizierte Funktion auch staticeinen Dateibereich, wie in C.
RobertS unterstützt Monica Cellio vor

0

Da statische Funktion nur in dieser Datei sichtbar ist. Tatsächlich kann der Compiler einige Optimierungen für Sie vornehmen , wenn Sie für eine Funktion "statisch" deklarieren.

Hier ist ein einfaches Beispiel.

Haupt c

#include <stdio.h>

static void test() 
{
    ghost(); // This is an unexist function.
}

int main()
{
    int ret = 0;

#ifdef TEST
#else
    test();
#endif
    return (ret);
} 

Und kompilieren mit

gcc -o main main.c

Sie werden sehen, dass es fehlgeschlagen ist. Weil Sie sogar die Ghost () - Funktion nicht implementieren.

Aber was ist, wenn wir den folgenden Befehl verwenden?

gcc -DTEST -O2 -o main main.c

Es ist erfolgreich , und dieses Programm kann normal ausgeführt werden.

Warum? Es gibt 3 wichtige Punkte.

  1. -O2: Compiler-Optimierungsstufe mindestens 2.
  2. -DTEST: Definieren Sie TEST, sodass test () nicht aufgerufen wird.
  3. Definiert "statisch" für test ().

Nur wenn diese drei Bedingungen erfüllt sind, können Sie die Kompilierung bestehen. Aufgrund dieser "statischen" Deklaration kann der Compiler bestätigen, dass test () NIEMALS in einer anderen Datei aufgerufen wird. Ihr Compiler kann test () beim Kompilieren entfernen. Da wir test () nicht benötigen, spielt es keine Rolle, ob ghost () definiert oder implementiert ist.


0

" Was ist eine" static"Funktion in C? "

Beginnen wir am Anfang.

Es basiert alles auf einer Sache namens "Verknüpfung":

" Ein Bezeichner, der mehr als einmal in verschiedenen Bereichen oder im selben Bereich deklariert wurde, kann durch einen Prozess namens Verknüpfung dazu gebracht werden, auf dasselbe Objekt oder dieselbe Funktion zu verweisen. 29) Es gibt drei Arten von Verknüpfungen: externe, interne und keine. "

Quelle: C18, 6.2.2 / 1


"In dem Satz von Übersetzungseinheiten und Bibliotheken, die ein gesamtes Programm bilden, bezeichnet jede Deklaration eines bestimmten Bezeichners mit externer Verknüpfung dasselbe Objekt oder dieselbe Funktion. Innerhalb einer Übersetzungseinheit bezeichnet jede Deklaration eines Bezeichners mit interner Verknüpfung dasselbe Objekt oder dieselbe Funktion Jede Deklaration eines Bezeichners ohne Verknüpfung bezeichnet eine eindeutige Entität. "

Quelle: C18, 6.2.2 / 2


Wenn eine Funktion ohne Speicherklassenspezifizierer definiert ist, verfügt die Funktion externstandardmäßig über eine Verknüpfung:

"Wenn die Deklaration eines Bezeichners für eine Funktion keinen Speicherklassenspezifizierer hat, wird ihre Verknüpfung genau so bestimmt, als ob sie mit dem Speicherklassenspezifizierer extern deklariert worden wäre ."

Quelle: C18, 6.2.2 / 5

Das bedeutet, dass - wenn Ihr Programm aus mehreren Übersetzungseinheiten / Quelldateien ( .coder .cpp) besteht - die Funktion in allen Übersetzungseinheiten / Quelldateien Ihres Programms sichtbar ist.

Dies kann in einigen Fällen ein Problem sein. Was ist, wenn Sie z. B. zwei verschiedene Funktionen (Definitionen) verwenden möchten, jedoch mit demselben Funktionsnamen in zwei verschiedenen Kontexten (eigentlich dem Dateikontext).

In C und C ++ hilft jetzt das staticQualifikationsmerkmal für die Speicherklasse , das auf eine Funktion im Dateibereich angewendet wird (keine statische Elementfunktion einer Klasse in C ++ oder eine Funktion in einem anderen Block), und zeigt an, dass die jeweilige Funktion nur innerhalb von sichtbar ist die Übersetzungseinheit / Quelldatei, in der sie definiert wurde, und nicht in den anderen TLUs / Dateien.

"Wenn die Deklaration eines Dateibereichsbezeichners für ein Objekt oder eine Funktion den statischen Speicherklassenspezifizierer enthält , ist der Bezeichner intern verknüpft. 30)"


30) Eine Funktionsdeklaration kann den Speicherklassenspezifizierer nur dann enthalten, wenn er sich im Dateibereich befindet. siehe 6.7.1.

Quelle: C18, 6.2.2 / 3


Eine staticFunktion ist also nur dann sinnvoll, wenn:

  1. Ihr Programm besteht aus mehreren Übersetzungseinheiten / Quelldateien ( .coder .cpp).

    und

  2. Sie möchten den Umfang einer Funktion auf die Datei beschränken, in der die spezifische Funktion definiert ist.

Wenn nicht beide Anforderungen übereinstimmen, müssen Sie sich nicht darum kümmern, eine Funktion als zu qualifizieren static.


Randnotizen:

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.