678 Stimmen

Was ist eine "statische" Funktion in C?

Die Frage bezog sich auf einfache c Funktionen, nicht c++ static Methoden, wie in den Kommentaren präzisiert.

Ich verstehe, was ein static Variable ist, aber was ist eine static Funktion?

Und warum ist es so, dass, wenn ich eine Funktion deklariere, sagen wir void print_matrix in, sagen wir mal a.c (OHNE a.h ) und umfassen "a.c" - Ich bekomme "print_matrix@@....) already defined in a.obj" , ABER wenn ich es deklariere als static void print_matrix dann kompiliert er?

UPDATE Nur um das klarzustellen - ich weiß, dass auch .c ist schlecht, wie viele von Ihnen betont haben. Ich tue es nur, um vorübergehend Platz zu schaffen in main.c bis ich eine bessere Vorstellung davon habe, wie ich all diese Funktionen zu richtigen .h y .c Dateien. Nur eine vorübergehende, schnelle Lösung.

872voto

Johannes Weiss Punkte 49967

static Funktionen sind Funktionen, die nur für andere Funktionen in derselben Datei sichtbar sind (genauer gesagt _derselbe Übersetzungseinheit_ ).

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

3 Stimmen

Eigentlich habe ich es nicht als C++ gekennzeichnet, einige Admins haben es wahrscheinlich getan, aber es ging um C++, was ist also der Unterschied in C++?

21 Stimmen

C++-Methoden werden oft als "Mitgliedsfunktionen" bezeichnet, daher stimme ich zu, dass C++ ein wenig Zweideutigkeit einführt. Das ist nicht Ihre Schuld - die Sprache verwendet das Schlüsselwort einfach für zwei verschiedene Dinge.

0 Stimmen

Das ist ja schön und gut, aber sind Sie nicht auch der Meinung, dass Slawa damit ganz klar eine normale Funktion meinte?

237voto

Dima Punkte 37984

Es gibt einen großen Unterschied zwischen statischen Funktionen in C und statischen Mitgliedsfunktionen in C++. In C ist eine statische Funktion außerhalb ihrer Übersetzungseinheit, d. h. der Objektdatei, in die sie kompiliert wurde, nicht sichtbar. Mit anderen Worten: Wenn eine Funktion statisch ist, wird ihr Anwendungsbereich eingeschränkt. Man kann sich eine statische Funktion so vorstellen, als sei sie "privat" für ihre *.c-Datei (obwohl das nicht ganz korrekt ist).

In C++ kann sich "static" auch auf Mitgliedsfunktionen und Datenmitglieder von Klassen beziehen. Ein statisches Datenmitglied wird auch als "Klassenvariable" bezeichnet, während ein nicht-statisches Datenmitglied eine "Instanzvariable" ist. Dies ist Smalltalk-Terminologie. Das bedeutet, dass es nur eine Kopie eines statischen Datenelements gibt, die von allen Objekten einer Klasse geteilt wird, während jedes Objekt seine eigene Kopie eines nicht-statischen Datenelements hat. Ein statisches Datenelement ist also im Wesentlichen eine globale Variable, die Mitglied einer Klasse ist.

Nicht-statische Mitgliedsfunktionen können auf alle Datenmitglieder der Klasse zugreifen: statische und nicht-statische. Statische Mitgliedsfunktionen können nur auf die statischen Datenmitglieder zugreifen.

Man kann sich das so vorstellen, dass in C++ statische Datenmitglieder und statische Mitgliedsfunktionen nicht zu einem beliebigen Objekt gehören, sondern zur gesamten Klasse.

51 Stimmen

C++ hat auch file-static. Es ist nicht nötig, C hier mit einzubeziehen.

23 Stimmen

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

4 Stimmen

Gibt es einen Unterschied zwischen einer globalen var und einer statischen var der Klasse (außer dem Namensraum)?

78voto

Beispiel für einen minimalen lauffähigen Bereich mit mehreren Dateien

Hier veranschauliche ich, wie static beeinflusst den Geltungsbereich von Funktionsdefinitionen über mehrere Dateien hinweg.

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();
}

main.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 vorgelagert .

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

Auslegung

  • Es gibt zwei getrennte Funktionen sf eine für jede Datei
  • es gibt eine einzige gemeinsame Funktion f

Wie üblich gilt: Je kleiner der Umfang, desto besser, also immer Funktionen deklarieren static wenn Sie können.

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

Ein gängiges C-Muster ist die Übergabe einer this struct als erstes "Methoden"-Argument, was im Grunde das ist, was C++ unter der Haube tut.

Was die Normen dazu sagen

C99 N1256 Entwurf 6.7.1 "Speicherklassenspezifizierer" besagt, dass static ist ein "Speicherklassenbezeichner".

6.2.2/3 "Verknüpfungen von Identifikatoren" sagt static impliziert internal linkage :

Wenn die Deklaration eines Dateibereichsbezeichners für ein Objekt oder eine Funktion den Speicherklassenspezifizierer static enthält, hat der Bezeichner eine interne Verknüpfung.

und 6.2.2/2 besagt, dass internal linkage verhält sich wie in unserem Beispiel:

In der Menge der Übersetzungseinheiten und Bibliotheken, die ein ganzes 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.

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

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

Mit dem STB_LOCAL Bindung.

Wenn wir kompilieren:

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

und demontieren 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 wesentliche Unterschied zwischen ihnen. Value ist nur ihr Offset in der .bss Abschnitt, so dass wir davon ausgehen, dass er sich unterscheiden wird.

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

STB_LOCAL Lokale Symbole sind außerhalb der Objektdatei, die ihre Definition enthält, nicht sichtbar. Lokale Symbole desselben Namens können in mehreren Dateien existieren, ohne sich gegenseitig zu behindern

was sie zu einer perfekten Wahl für die Darstellung von static .

Funktionen ohne Statik sind STB_GLOBAL und in der Spezifikation steht:

Wenn der Link-Editor mehrere verschiebbare Objektdateien kombiniert, lässt er keine Mehrfachdefinitionen von STB_GLOBAL-Symbolen mit demselben Namen zu.

was mit den Verknüpfungsfehlern bei mehreren nicht statischen Definitionen kohärent ist.

Wenn wir die Optimierung mit -O3 die sf Symbol wird komplett aus der Symboltabelle entfernt: es kann von außen sowieso nicht verwendet werden. TODO Warum sollten statische Funktionen überhaupt in der Symboltabelle verbleiben, wenn es keine Optimierung gibt? Können sie für irgendetwas verwendet werden?

Siehe auch

C++ anonyme Namespaces

In C++ können Sie anonyme Namespaces anstelle von statischen Namespaces verwenden, wodurch ein ähnlicher Effekt erzielt wird, die Typdefinitionen aber noch weiter versteckt werden: Unbenannte/anonyme Namensräume vs. statische Funktionen

3 Stimmen

Anmerkung: void f() { puts("sf"); } (d.h. zwei Definitionen von f() ) führt zu undefiniertem Verhalten, ohne dass eine Diagnose erforderlich ist. Es ist ein Qualitätsproblem, eine Fehlermeldung zu sehen.

21voto

dersimn Punkte 775

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 überhaupt keinen Unterschied. Der Unterschied kommt bei größeren Projekten mit mehreren Dateien zum Tragen:

In C wird jedes "Modul" (eine Kombination aus sample.c und sample.h) unabhängig kompiliert, und anschließend werden diese kompilierten Objektdateien (sample.o) vom Linker zu einer ausführbaren Datei zusammengefügt.

Nehmen wir an, Sie haben mehrere Dateien, die Sie in Ihre Hauptdatei einbinden, und zwei davon haben eine Funktion, die nur intern aus Gründen der Bequemlichkeit verwendet wird, nämlich add(int a, b) - der Compiler würde problemlos Objektdateien für diese beiden Module erstellen, aber der Linker wird einen Fehler ausgeben, weil er zwei Funktionen mit demselben Namen findet und nicht weiß, welche er verwenden soll (selbst wenn es nichts zu linken gibt, weil sie nirgendwo anders verwendet werden als in der eigenen Datei).

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

17voto

raimue Punkte 4182

Bei statischen Funktionsdefinitionen wird dieses Symbol als intern gekennzeichnet. Es ist also nicht für die Verknüpfung von außen sichtbar, sondern nur für Funktionen in derselben Kompiliereinheit, in der Regel in derselben Datei.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X