wake-up-neo.com

Was ist eine "statische" Funktion?

Die Frage betraf einfache c Funktionen, nicht c ++static Methoden, wie in Kommentaren erläutert.

Ok, ich verstehe, was eine static Variable ist, aber was ist eine static Funktion?

Und warum ist es so, dass wenn ich eine Funktion deklariere, lass uns void print_matrix sagen, in lass uns a.c sagen (OHNE a.h) und "a.c" einschließen - ich bekomme "[email protected]@....) already defined in a.obj" , ABER wenn ich es als static void print_matrix deklariere, kompiliert es dann?

UPDATE Nur um die Dinge zu klären - ich weiß, dass es schlecht ist, .c einzuschließen, wie viele von Ihnen darauf hingewiesen haben. Ich mache das nur, um vorübergehend Speicherplatz in main.c freizugeben, bis ich eine bessere Vorstellung davon habe, wie ich all diese Funktionen in richtige .h und .c Dateien gruppieren kann. Nur eine vorübergehende, schnelle Lösung.

454
Slava V

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

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

622
Johannes Weiss

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, dh der Objektdatei, in die sie kompiliert wird, nicht sichtbar. Mit anderen Worten, wenn Sie eine Funktion statisch machen, 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 "static" auch auf Memberfunktionen und Datenmember von Klassen angewendet werden. 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 eine eigene Kopie eines nicht statischen Datenelements hat. Ein statisches Datenelement ist also im Wesentlichen eine globale Variable, dh ein Element einer Klasse.

Nicht statische Elementfunktionen können auf alle Datenelemente der Klasse zugreifen: statisch und nicht statisch. Statische Elementfunktionen können nur auf die statischen Datenelemente angewendet werden.

In C++ gehören statische Datenmember und statische Memberfunktionen nicht zu einem Objekt, sondern zur gesamten Klasse.

187
Dima

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

Die erste besteht darin, die Funktion als mit einer internen Verknüpfung zu kennzeichnen, damit auf sie in anderen Übersetzungseinheiten nicht verwiesen 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 Member-Funktion hat, bedeutet dies, dass die Funktion ein Member der Klasse ist (und den normalen Zugriff auf andere Member hat), aber nicht über ein bestimmtes Objekt aufgerufen werden muss. Mit anderen Worten, innerhalb dieser Funktion gibt es keinen "dieser" Zeiger.

74
Brian Neal

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

Hier zeige ich, wie static den Umfang der Funktionsdefinitionen über mehrere Dateien hinweg beeinflusst.

a.c.

#include <stdio.h>

/* Undefined behavior: already defined in main.
 * Binutils 2.24 gives an error and refuses to link.
 * https://stackoverflow.com/questions/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 upstream .

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

Interpretation

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

Je kleiner der Bereich, desto besser, deklarieren Sie wie üblich die Funktionen static, wenn Sie können.

In der C-Programmierung werden Dateien häufig verwendet, um "Klassen" darzustellen, und static -Funktionen stellen "private" Methoden der Klasse dar.

Ein gängiges C-Muster besteht darin, eine this -Struktur als erstes Argument für "method" zu übergeben. Dies ist im Grunde das, was C++ unter der Haube tut.

Was Normen dazu sagen

Entwurf für C99 N1256 6.7.1 "Speicherklassenspezifizierer" besagt, dass static ein "Speicherklassenspezifizierer" ist.

6.2.2/3 "Verknüpfungen von Bezeichnern" besagt static impliziert internal linkage:

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

und 6.2.2/2 besagt, dass sich internal linkage 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 Bindung STB_LOCAL.

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. Value ist nur ihr Offset im Abschnitt .bss, daher erwarten wir, dass er sich unterscheidet.

STB_LOCAL ist 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, static darzustellen.

Funktionen ohne Statik sind STB_GLOBAL, und die Spezifikation sagt:

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

dies stimmt mit den Verbindungsfehlern bei mehreren nicht statischen Definitionen überein.

Wenn wir die Optimierung mit -O3 ankurbeln, wird das Symbol sf vollständig aus der Symboltabelle entfernt: Es kann sowieso nicht von außen verwendet werden. Warum sollten statische Funktionen in der Symboltabelle überhaupt beibehalten werden, 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. Außerdem werden Typdefinitionen ausgeblendet: nbenannte/anonyme Namespaces vs. statische Funktionen

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 werden alle kompilierten Objektdateien (sample.o) vom Linker zu einer ausführbaren Datei verknüpft.

Angenommen, Sie haben mehrere Dateien, die Sie in Ihre Hauptdatei aufnehmen, und zwei von ihnen haben eine Funktion, die nur intern verwendet wird und add(int a, b) heißt. Der Compiler würde leicht Objektdateien für diese beiden Module erstellen, der Linker jedoch einen Fehler auslösen, weil es zwei Funktionen mit demselben Namen findet und nicht weiß, welche es verwenden soll (auch wenn es nichts zu verlinken gibt, weil sie nicht woanders verwendet werden, sondern in seiner eigenen Datei).

Deshalb machen Sie diese Funktion, die nur intern verwendet wird, zu einer statischen Funktion. In diesem Fall erzeugt der Compiler nicht das typische "you can link this thing" -Flag für den Linker, so dass der Linker diese Funktion nicht sieht und keinen Fehler erzeugt.

19
dersimn

Erstens: Es ist im Allgemeinen eine schlechte Idee, eine _.cpp_ -Datei 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 es nicht, bis ich in Kommentaren darauf hingewiesen habe.

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 hat a.obj eine Kopie und Ihr anderer Code eine unabhängige Kopie. (Aufblähen der endgültigen ausführbaren Datei mit mehreren Kopien des Codes).

b) static member function - welche Objektorientierung bezeichnet 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 seid Drachen.

16
Douglas Leeder

statische Funktionsdefinitionen kennzeichnen dieses Symbol als intern. Es ist also nicht für Links von außen sichtbar, sondern nur für Funktionen in derselben Zusammenstellungseinheit, normalerweise dieselbe Datei.

14
raimue

Eine statische Funktion kann für die Klasse selbst im Gegensatz zu einer Instanz der Klasse 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 auch ohne Instanz funktioniert. Dies wird manchmal im Factory-Muster verwendet:

Person* tom = Person::createNewPerson();
8
Parrots

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 in 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.

7
user2410022

Kleinigkeit: Statische Funktionen sind für eine Übersetzungseinheit sichtbar. In den meisten 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 Nicht-Inline-Funktion oder jedes Nicht-Inline-Objekts enthalten, das in diesem Programm verwendet wird. Es ist keine Diagnose erforderlich."

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

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

Ich würde 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 zu erhalten/zu verwenden.

6
dirkgently