Beispiel für einen minimalen ausführbaren Bereich mit mehreren Dateien
Hier zeige ich, wie static
sich 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 static
Sie daher immer Funktionen, wenn Sie können.
In der C-Programmierung werden Dateien häufig verwendet, um "Klassen" static
darzustellen , und Funktionen repräsentieren "private" Methoden der Klasse.
Ein übliches C-Muster besteht darin, eine this
Struktur 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 static
ein "Speicherklassenspezifizierer" ist.
6.2.2 / 3 "Verknüpfungen von Kennungen" static
impliziert 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 linkage
sich 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_LOCAL
Bindung.
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 Versatz in den .bss
Abschnitt, also erwarten wir, dass es anders ist.
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 für die Darstellung static
.
Funktionen ohne statische Aufladung sind STB_GLOBAL
und 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 sf
Symbol 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