2010-05-30 6 views
17

Ich habe auf C++ im Internet gelesen, und hier ist eine Sache, auf die ich nicht wirklich eine Antwort finden konnte.Statische Variablen innerhalb von Funktionen in C++ - auch wenn die Funktion nicht ausgeführt wird?

Ich weiß, dass statische Variablen innerhalb Funktionen verwandt sind, um Globals, und dass nachfolgende Aufrufe dieser Funktion haben die statische Variable behalten ihren Wert zwischen den Aufrufen.

Wenn die Funktion jedoch nie aufgerufen wird, wird die statische Variable zugewiesen?

Danke

+0

Dies ist ein guter Grund, Funktionen in eine separate Übersetzungseinheit zu stellen; damit nie aufgerufene Funktionen zum Zeitpunkt der Erstellung entfernt werden können. :-) –

+0

Sie müssen zwischen __allocation__ und __initialization__ unterscheiden.Wenn eine nicht aufgerufene Funktion nicht vom Linker entfernt wird, ist es wahrscheinlich, dass der Speicher für diese Variable _allocated_ ist, obwohl sie niemals initialisiert wird. – sbi

+0

@Thomas: Es gibt einen guten Grund, einen Compiler zu wählen, der nicht so viel Handholding benötigt. Es ist kein Hexenwerk herauszufinden, ob 'void foo()' aufgerufen wird. – MSalters

Antwort

16

Wenn die Funktion nie aufgerufen wird, ist es wahrscheinlich, dass Ihr Linker deadstrip sowohl die Funktion und die statische Variable, es das Eindringen von .rodata, .data zu verhindern oder .bss Segmente (oder die ausführbare Datei Äquivalente des Dateiformat).

Es gibt jedoch verschiedene Gründe, warum ein Linker möglicherweise nicht deadstrip (Flags, die es nicht zu sagen, eine Unfähigkeit, zu bestimmen, was vom Symbol abhängt, etc).

Es lohnt Linker-Map-Datei überprüft (manchmal nur eine Textdatei!) Oder mit objdump, nm oder dumpbin Dienstprogramme auf die endgültige ausführbare Datei, um zu sehen, ob das Symbol oder verwandte Symbole (wie statische Initialisierer-Code) überlebt.

+0

+1 Für die Erwähnung von Deadstrip und die Möglichkeit, dass der Linker die Variable (n) zusammen mit der Funktion entfernen wird. –

+2

* Hinweis: .rodata, .data und .bss sind unix-spezifisch. (aber +1) –

+0

@Billy ONeal: System V ABI, ja? Ich frage mich, was die Geschichte dieser Segmente ist ... Ich nahm an, dass sie in ELF eingebaut wurden, aber Sie können (und wir tun) immer benutzerdefinierte Segmente angeben, und das ELF-Format scheint nicht besonders behandelt zu werden gemeinsame (abgesehen davon, dass verschiedene 'SHT_ *' - Flags usw. unterschiedlich gesetzt werden). Hat jemand eine Referenz für die entsprechenden Fensterabschnitte? (Vielleicht irgendwo in der PE/COFF Dokumentation? Zeit zu graben ...) – leander

1

Ich bin mir sicher, dass das bis zur Umsetzung wird. Was MSVC tut ist - statische Objekte sind im automatischen Datensegment der EXE oder DLL zugeordnet. Der Konstruktor wird jedoch nur ausgeführt, wenn die Funktion, die das Static enthält, zum ersten Mal ausgeführt wird.

8

Die C++ Standard Abschnitt 6.7 sagt:

Die Null-Initialisierung (8,5) alle lokalen Objekte mit statischen Speichern Dauer (3.7.1) durchgeführt wird, bevor andere Initialisierung stattfindet. Ein lokales Objekt vom POD-Typ (3.9) mit Statische Speicherdauer initialisiert mit konstanten Ausdrücken ist initialisiert, bevor sein Block zuerst eingegeben wird. Eine Implementierung ist zulässige Form frühe Initialisierung von anderen lokalen Objekten mit statischer Lagerdauer unter den gleichen Bedingungen per- dass eine Implementierung statisch erlaubt ist ein Objekt mit statischer Speicherdauer in Namespacebereich zu initialisieren (3.6.2). Sonst ein solches Objekt initialisiert wird die erste Zeit Kontrolle durchläuft seine Deklaration; ein solches Objekt wird als initialisiert auf den Abschluss seiner Initialisierung betrachtet.

Dies zeigt an, dass lokale statische Objekte normalerweise initialisiert werden, wenn der Kontrollfluss das erste Mal auf sie stößt. Sie können jedoch vorher gut zugeordnet werden - der Standard ist etwas zurückhaltend bezüglich des statischen Speichers, außer in Bezug auf die Lebensdauer von statischen Objekten.

0

Ja, tatsächliche Zuordnung ist Compiler-abhängig, obwohl ich denke, dass jeder Compiler reserviert nur den Speicherplatz im .static-Segment der ausführbaren Datei (oder das Äquivalent in seinem ausführbaren Dateiformat).
Die Initialisierung findet jedoch erst zum ersten Mal statt, wenn der Ausführungsablauf auf das statische Objekt stößt, und dies wird vom Standard verlangt.
Beachten Sie, dass die Initialisierung von global statische Objekte auf eine andere Weise funktioniert. Sie können sehr gute Antworten auf fast jede Frage auf der C++ FAQ lite Website erhalten. Ich mag auch Scott Meyers "Effective C++".

0

Hängt davon ab. Wenn Sie meinen, nie aufgerufen, wie in, die Funktion wird buchstäblich nie aufgerufen, dann wird Ihr Compiler wahrscheinlich nicht zuordnen, oder sogar den Funktionscode eingeben. Wenn Sie es jedoch abhängig machen von, sagen wir, Benutzereingaben, und dass Benutzereingaben einfach nie auftauchen, dann wird es wahrscheinlich vorher zugewiesen werden. Allerdings treten Sie hier in ein Minenfeld ein, und es ist am besten anzunehmen, dass es immer von der Zeit erstellt wird, zu der die Kontrolle die Funktion (en) eingibt, die sich darauf beziehen.

4

Jedes Objekt in C++ hat zwei verschachtelte Zeitperioden mit ihm verbunden: Lagerdauer und Lebensdauer. Speicherdauer ist der Zeitraum, für den der vom Objekt belegte Rohspeicher zugewiesen wird. Lebensdauer ist der Zeitraum zwischen Konstruktion und Zerstörung eines tatsächlichen Objekts in diesem Speicher. (Für Objekte von POD-Typen spielt die Konstruktionszerstörung entweder keine Rolle oder ist nicht anwendbar, so dass ihre Lebensdauer der Speicherdauer entspricht).

Wenn jemand "zugewiesen" sagt, beziehen sie sich normalerweise auf Speicherdauer. Die Sprache spezifiziert nicht genau, wann die Speicherdauer des Objekts beginnt. Es ist ausreichend zu fordern, dass es an einem Punkt beginnt, bevor die Lebensdauer des Objekts beginnt beginnt.

Aus diesem Grund beginnt im Allgemeinen ein statisches Objekt, das in einer Funktion definiert ist, niemals seine Lebensdauer und theoretisch muss die Speicherdauer auch nicht beginnen. Also, in der Theorie, in vielleicht nicht einmal "zugewiesen".

In der Praxis werden jedoch alle Objekte mit statischer Speicherdauer ("Globals", lokale Statik usw.) normalerweise gleich behandelt: Sie erhalten beim Start des Programms eine bestimmte Speichermenge.


Als zusätzliche Anmerkung, wenn ein lokales Objekt mit statischer Lagerdauer eine nicht-triviale Initialisierung erfordert, wird diese Initialisierung durchgeführt, wenn die Kontrolle über die Definition gelangt zum ersten Mal. So in diesem Beispiel

void foo() { 
    static int *p = new int[100]; 
} 

das dynamische Array nie, wenn die Funktion aufgerufen wird nie zugewiesen werden. Und es wird nur einmal zugewiesen, wenn die Funktion aufgerufen wird. Das sieht nicht nach dem aus, was Sie fragen, aber ich erwähne das nur für den Fall.

+0

Dies scheint wahr zu sein (p wird erst beim ersten Aufruf der Funktion initialisiert). Das ist irgendwie komisch: Was passiert, wenn die Funktion foo von einem beliebigen Thread aufgerufen wird? Sagen wir, wir haben 100 Threads, die irgendwann foo aufrufen müssen ... Welcher Thread wird p initialisieren? Es wird eine Wettlaufbedingung sein. Wie auch immer, es ist eine schlechte Übung, solche statischen Objekte zu verwenden, da es sehr schwierig ist, sie zu initialisieren. – botismarius

0

Statische Variablen, die für Klassen (Elemente) oder Funktionen definiert sind, werden beim Funktionsaufruf nicht dynamisch auf dem Stack zugewiesen, wie nicht statische. Sie sind in einem anderen Bereich des generierten Codes reserviert, der für globale und statische Daten reserviert ist. Wenn Sie also die Funktion aufrufen oder Klassen instanziieren, die statische Member enthalten oder nicht, wird ein Speicherplatz für ihre Daten ohnehin im Programmdatenbereich reserviert.

Verwandte Themen