Wenn Sie eine Funktion staticerstellen, wird sie vor anderen Übersetzungseinheiten ausgeblendet, wodurch die Kapselung erleichtert wird .
helper_file.c
int f1(int);/* prototype */staticint f2(int);/* prototype */int f1(int foo){return f2(foo);/* ok, f2 is in the same translation unit *//* (basically same .c file) as f1 */}int f2(int foo){return42+ foo;}
main.c :
int f1(int);/* prototype */int f2(int);/* prototype */int main(void){
f1(10);/* ok, f1 is visible to the linker */
f2(12);/* nope, f2 is not visible to the linker */return0;}
Ist die Übersetzungseinheit die richtige Terminologie? Wäre die Objektdatei nicht genauer? Soweit ich weiß, ist dem Linker eine statische Funktion verborgen, und der Linker arbeitet nicht mit Übersetzungseinheiten.
Steven Eckhoff
2
Ich hätte auch sagen sollen, dass ich es gerne als vor dem Linker verborgen betrachte; es scheint so klarer.
Steven Eckhoff
1
Also, interne Funktion (die wir sicher nicht außerhalb der c-Datei aufrufen dürfen), wir sollten sie als statische Funktion setzen, oder? Wir können also sicher sein, dass es nicht anderswo anrufen kann. Danke :)
hqt
1
Wie kompilierst du das? Haben Sie verwenden #include <helper_file.c>? Ich denke, das würde es dann zu einer einzigen Übersetzungseinheit machen ...
Atcold
2
@Atcold: So wie ich den Code geschrieben habe, fügen Sie einfach die 2 Quelldateien wie folgt in die Befehlszeile ein gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c. Die Prototypen für die Funktionen sind in beiden Quelldateien vorhanden (keine Header-Dateien erforderlich). Der Linker löst die Funktionen auf.
PMG
80
pmg ist genau richtig in Bezug auf die Einkapselung; Neben dem Ausblenden der Funktion vor anderen Übersetzungseinheiten (oder besser gesagt aufgrund dessen) kann das Erstellen von Funktionen staticauch bei Compileroptimierungen Leistungsvorteile bringen.
Da eine staticFunktion von keiner Stelle außerhalb der aktuellen Übersetzungseinheit aufgerufen werden kann (es sei denn, der Code nimmt einen Zeiger auf seine Adresse), steuert der Compiler alle Aufrufpunkte darin.
Dies bedeutet, dass es kostenlos ist, ein nicht standardmäßiges ABI zu verwenden, es vollständig zu integrieren oder eine beliebige Anzahl anderer Optimierungen durchzuführen, die für eine Funktion mit externer Verknüpfung möglicherweise nicht möglich sind.
... es sei denn, die Adresse der Funktion wird übernommen.
Café
1
@caf Was meinst du mit der Adresse der Funktion? Für mich ist die Vorstellung, dass Funktionen / Variablen Adressen haben oder zur Kompilierungszeit eine Adresse zugewiesen bekommen, etwas verwirrend. Können Sie bitte näher darauf eingehen?
SayeedHussain
2
@crypticcoder: Ihr Programm wird in den Speicher geladen, daher haben Funktionen auch einen Speicherort und die Adresse kann abgerufen werden. Mit einem Funktionszeiger können Sie jeden dieser Aufrufe aufrufen. Wenn Sie dies tun, wird die Liste der Optimierungen reduziert, die der Compiler ausführen kann, da der Code an derselben Stelle intakt bleiben muss.
5
@crypticcoder: Ich meine, ein Ausdruck wertet einen Zeiger auf die Funktion aus und macht etwas anderes damit, als die Funktion sofort aufzurufen. Wenn ein Zeiger auf eine staticFunktion der aktuellen Übersetzungseinheit entgeht, kann diese Funktion direkt von anderen Übersetzungseinheiten aufgerufen werden.
Café
@caf Wenn die Adresse der Funktion verwendet wird, würde der Compiler dies erkennen und die in dieser Antwort erwähnten statischen Funktionsoptimierungen deaktivieren (z. B. unter Verwendung eines nicht standardmäßigen ABI)? Ich nehme an, es müsste sein.
Sevko
28
Das staticSchlüsselwort in C wird in einer kompilierten Datei verwendet (.c im Gegensatz zu .h), sodass die Funktion nur in dieser Datei vorhanden ist.
Normalerweise generiert der Compiler beim Erstellen einer Funktion eine Cruft, mit der der Linker einen Funktionsaufruf mit dieser Funktion verknüpfen kann. Wenn Sie das Schlüsselwort static verwenden, können andere Funktionen in derselben Datei diese Funktion aufrufen (da dies ohne Rückgriff auf den Linker möglich ist), während der Linker keine Informationen hat, mit denen andere Dateien auf die Funktion zugreifen können.
C-Programmierer verwenden das statische Attribut, um Variablen- und Funktionsdeklarationen in Modulen auszublenden, ähnlich wie Sie öffentliche und private Deklarationen in Java und C ++ verwenden würden. C-Quelldateien spielen die Rolle von Modulen. Jede globale Variable oder Funktion, die mit dem statischen Attribut deklariert wurde, ist für dieses Modul privat. Ebenso ist jede globale Variable oder Funktion, die ohne das statische Attribut deklariert wurde, öffentlich und kann von jedem anderen Modul aufgerufen werden. Es ist eine gute Programmierpraxis, Ihre Variablen und Funktionen nach Möglichkeit mit dem statischen Attribut zu schützen.
Die Antwort von pmg ist sehr überzeugend. Wenn Sie wissen möchten, wie statische Deklarationen auf Objektebene funktionieren, könnten diese Informationen für Sie interessant sein. Ich habe dasselbe von pmg geschriebene Programm wiederverwendet und es in eine .so-Datei (Shared Object) kompiliert
Der folgende Inhalt befindet sich nach dem Ablegen der .so-Datei in einer für Menschen lesbaren Datei
0000000000000675 f1 : Adresse der Funktion f1
000000000000068c f2 : Adresse der Funktion f2 (staticc)
Beachten Sie den Unterschied in der Funktionsadresse, es bedeutet etwas. Für eine Funktion, die mit einer anderen Adresse deklariert ist, kann dies sehr gut bedeuten, dass f2 sehr weit entfernt oder in einem anderen Segment der Objektdatei lebt.
Linker verwenden PLT (Procedure Linkage Table) und GOT (Global Offsets Table), um Symbole zu verstehen, auf die sie zugreifen können.
Denken Sie vorerst, dass GOT und PLT alle Adressen auf magische Weise binden und ein dynamischer Abschnitt Informationen zu all diesen Funktionen enthält, die für den Linker sichtbar sind.
Nach dem Speichern des dynamischen Abschnitts der .so-Datei erhalten wir eine Reihe von Einträgen, die jedoch nur an den Funktionen f1 und f2 interessiert sind .
Der dynamische Abschnitt enthält nur den Eintrag für die Funktion f1 unter der Adresse 0000000000000675 und nicht für die Funktion f2 !
Num: Wert Größe Typ Bind Vis Ndx Name
9:000000000000067523 FUNC GLOBAL DEFAULT 11 f1
Und das ist es !. Daraus wird deutlich, dass der Linker die f2- Funktion nicht finden kann, da sie sich nicht im dynamischen Abschnitt der .so-Datei befindet.
Wenn der Zugriff auf einige Funktionen eingeschränkt werden muss, verwenden wir das statische Schlüsselwort, um eine Funktion zu definieren und zu deklarieren.
/* file ab.c */staticvoid function1(void){
puts("function1 called");}And store the following code in another file ab1.c
/* file ab1.c */int main(void){
function1();
getchar();return0;}/* in this code, we'll get a "Undefined reference to function1".Because function 1 is declared static in file ab.c and can't be used in ab1.c */
Antworten:
Wenn Sie eine Funktion
static
erstellen, wird sie vor anderen Übersetzungseinheiten ausgeblendet, wodurch die Kapselung erleichtert wird .helper_file.c
main.c :
quelle
#include <helper_file.c>
? Ich denke, das würde es dann zu einer einzigen Übersetzungseinheit machen ...gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c
. Die Prototypen für die Funktionen sind in beiden Quelldateien vorhanden (keine Header-Dateien erforderlich). Der Linker löst die Funktionen auf.pmg ist genau richtig in Bezug auf die Einkapselung; Neben dem Ausblenden der Funktion vor anderen Übersetzungseinheiten (oder besser gesagt aufgrund dessen) kann das Erstellen von Funktionen
static
auch bei Compileroptimierungen Leistungsvorteile bringen.Da eine
static
Funktion von keiner Stelle außerhalb der aktuellen Übersetzungseinheit aufgerufen werden kann (es sei denn, der Code nimmt einen Zeiger auf seine Adresse), steuert der Compiler alle Aufrufpunkte darin.Dies bedeutet, dass es kostenlos ist, ein nicht standardmäßiges ABI zu verwenden, es vollständig zu integrieren oder eine beliebige Anzahl anderer Optimierungen durchzuführen, die für eine Funktion mit externer Verknüpfung möglicherweise nicht möglich sind.
quelle
static
Funktion der aktuellen Übersetzungseinheit entgeht, kann diese Funktion direkt von anderen Übersetzungseinheiten aufgerufen werden.Das
static
Schlüsselwort in C wird in einer kompilierten Datei verwendet (.c im Gegensatz zu .h), sodass die Funktion nur in dieser Datei vorhanden ist.Normalerweise generiert der Compiler beim Erstellen einer Funktion eine Cruft, mit der der Linker einen Funktionsaufruf mit dieser Funktion verknüpfen kann. Wenn Sie das Schlüsselwort static verwenden, können andere Funktionen in derselben Datei diese Funktion aufrufen (da dies ohne Rückgriff auf den Linker möglich ist), während der Linker keine Informationen hat, mit denen andere Dateien auf die Funktion zugreifen können.
quelle
In den obigen Beiträgen möchte ich auf ein Detail hinweisen.
Angenommen, unsere Hauptdatei ("main.c") sieht folgendermaßen aus:
Betrachten Sie nun drei Fälle:
Fall 1: Unsere Header-Datei ("header.h") sieht folgendermaßen aus:
Dann der folgende Befehl unter Linux:
wird gelingen ! Danach, wenn man rennt
Die Ausgabe wird sein
Aufruffunktion im Header
Welches ist, was diese statische Funktion drucken sollte.
Fall 2: Unsere Header-Datei ("header.h") sieht folgendermaßen aus:
und wir haben auch noch eine Datei "header.c", die so aussieht:
Dann den folgenden Befehl
wird einen Fehler geben.
Fall 3:
Ähnlich wie in Fall 2, außer dass jetzt unsere Header-Datei ("header.h") lautet:
Dann ist der gleiche Befehl wie in Fall 2 erfolgreich, und eine weitere Ausführung von ./main ergibt das erwartete Ergebnis.
Aus diesen Tests (ausgeführt auf einem Acer x86-Computer, Ubuntu OS) ging ich davon aus, dass
Das statische Schlüsselwort verhindert, dass Funktionen in einer anderen * .c-Datei als der definierten aufgerufen werden.
Korrigieren Sie mich, wenn ich falsch liege.
quelle
C-Programmierer verwenden das statische Attribut, um Variablen- und Funktionsdeklarationen in Modulen auszublenden, ähnlich wie Sie öffentliche und private Deklarationen in Java und C ++ verwenden würden. C-Quelldateien spielen die Rolle von Modulen. Jede globale Variable oder Funktion, die mit dem statischen Attribut deklariert wurde, ist für dieses Modul privat. Ebenso ist jede globale Variable oder Funktion, die ohne das statische Attribut deklariert wurde, öffentlich und kann von jedem anderen Modul aufgerufen werden. Es ist eine gute Programmierpraxis, Ihre Variablen und Funktionen nach Möglichkeit mit dem statischen Attribut zu schützen.
quelle
Die Antwort von pmg ist sehr überzeugend. Wenn Sie wissen möchten, wie statische Deklarationen auf Objektebene funktionieren, könnten diese Informationen für Sie interessant sein. Ich habe dasselbe von pmg geschriebene Programm wiederverwendet und es in eine .so-Datei (Shared Object) kompiliert
Der folgende Inhalt befindet sich nach dem Ablegen der .so-Datei in einer für Menschen lesbaren Datei
0000000000000675 f1 : Adresse der Funktion f1
000000000000068c f2 : Adresse der Funktion f2 (staticc)
Beachten Sie den Unterschied in der Funktionsadresse, es bedeutet etwas. Für eine Funktion, die mit einer anderen Adresse deklariert ist, kann dies sehr gut bedeuten, dass f2 sehr weit entfernt oder in einem anderen Segment der Objektdatei lebt.
Linker verwenden PLT (Procedure Linkage Table) und GOT (Global Offsets Table), um Symbole zu verstehen, auf die sie zugreifen können.
Denken Sie vorerst, dass GOT und PLT alle Adressen auf magische Weise binden und ein dynamischer Abschnitt Informationen zu all diesen Funktionen enthält, die für den Linker sichtbar sind.
Nach dem Speichern des dynamischen Abschnitts der .so-Datei erhalten wir eine Reihe von Einträgen, die jedoch nur an den Funktionen f1 und f2 interessiert sind .
Der dynamische Abschnitt enthält nur den Eintrag für die Funktion f1 unter der Adresse 0000000000000675 und nicht für die Funktion f2 !
Num: Wert Größe Typ Bind Vis Ndx Name
Und das ist es !. Daraus wird deutlich, dass der Linker die f2- Funktion nicht finden kann, da sie sich nicht im dynamischen Abschnitt der .so-Datei befindet.
quelle
Wenn der Zugriff auf einige Funktionen eingeschränkt werden muss, verwenden wir das statische Schlüsselwort, um eine Funktion zu definieren und zu deklarieren.
quelle