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