Andere Antworten vermeiden es einfach zu erklären, was statische Zuweisung bedeutet. Daher werde ich im Folgenden die drei Hauptformen der Zuweisung erklären und wie sie sich normalerweise auf den Heap, den Stack und das Datensegment beziehen. Ich werde auch einige Beispiele in C/C++ und Python zeigen, um das Verständnis zu erleichtern.
"Statische" (AKA statisch zugewiesene) Variablen werden nicht auf dem Stack zugewiesen. Nehmen Sie das nicht an - viele Leute tun das nur, weil "statisch" so ähnlich wie "Stack" klingt. Tatsächlich existieren sie weder auf dem Stack noch auf dem Heap. Sie sind Teil dessen, was man den Datensegment .
Im Allgemeinen ist es jedoch besser, die " scope " und " Lebensdauer " statt "Stack" und "Heap".
Der Geltungsbereich bezieht sich darauf, welche Teile des Codes auf eine Variable zugreifen können. Im Allgemeinen denken wir an lokaler Geltungsbereich (kann nur von der aktuellen Funktion aufgerufen werden) gegenüber globale Reichweite (auf die überall zugegriffen werden kann), obwohl der Umfang viel komplexer werden kann.
Die Lebensdauer bezieht sich darauf, wann eine Variable während der Programmausführung zugewiesen und freigegeben wird. Normalerweise denken wir an statische Zuordnung (die Variable bleibt während der gesamten Dauer des Programms erhalten und ist daher nützlich, um dieselben Informationen über mehrere Funktionsaufrufe hinweg zu speichern) versus automatische Zuweisung (Variable bleibt nur während eines einzigen Funktionsaufrufs bestehen und ist daher nützlich, um Informationen zu speichern, die nur während der Funktion verwendet werden und nach Beendigung gelöscht werden können) versus dynamische Zuteilung (Variablen, deren Dauer zur Laufzeit definiert wird, anstatt zur Kompilierzeit wie bei statischen oder automatischen Variablen).
Obwohl die meisten Compiler und Interpreter dieses Verhalten in Bezug auf die Verwendung von Stacks, Heaps usw. ähnlich umsetzen, kann ein Compiler diese Konventionen manchmal brechen, solange das Verhalten korrekt ist. So kann beispielsweise eine lokale Variable aus Optimierungsgründen nur in einem Register vorhanden sein oder ganz entfernt werden, obwohl die meisten lokalen Variablen auf dem Stack liegen. Wie bereits in einigen Kommentaren erwähnt wurde, steht es Ihnen frei, einen Compiler zu implementieren, der weder einen Stack noch einen Heap verwendet, sondern andere Speichermechanismen (was nur selten geschieht, da Stacks und Heaps dafür hervorragend geeignet sind).
Zur Veranschaulichung werde ich einen einfachen kommentierten C-Code zur Verfügung stellen. Am besten lernen Sie, indem Sie ein Programm mit einem Debugger ausführen und das Verhalten beobachten. Wenn Sie lieber Python lesen, springen Sie zum Ende der Antwort :)
// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;
// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;
// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {
// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed only within MyFunction()
static int someLocalStaticVariable;
// Allocated on the stack each time MyFunction is called
// Deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
int someLocalVariable;
// A *pointer* is allocated on the stack each time MyFunction is called
// This pointer is deallocated when MyFunction returns
// scope - the pointer can be accessed only within MyFunction()
int* someDynamicVariable;
// This line causes space for an integer to be allocated in the heap
// when this line is executed. Note this is not at the beginning of
// the call to MyFunction(), like the automatic variables
// scope - only code within MyFunction() can access this space
// *through this particular variable*.
// However, if you pass the address somewhere else, that code
// can access it too
someDynamicVariable = new int;
// This line deallocates the space for the integer in the heap.
// If we did not write it, the memory would be "leaked".
// Note a fundamental difference between the stack and heap
// the heap must be managed. The stack is managed for us.
delete someDynamicVariable;
// In other cases, instead of deallocating this heap space you
// might store the address somewhere more permanent to use later.
// Some languages even take care of deallocation for you... but
// always it needs to be taken care of at runtime by some mechanism.
// When the function returns, someArgument, someLocalVariable
// and the pointer someDynamicVariable are deallocated.
// The space pointed to by someDynamicVariable was already
// deallocated prior to returning.
return;
}
// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.
Ein besonders anschauliches Beispiel dafür, warum es wichtig ist, zwischen Lebensdauer und Geltungsbereich zu unterscheiden, ist die Tatsache, dass eine Variable einen lokalen Geltungsbereich, aber eine statische Lebensdauer haben kann - zum Beispiel "someLocalStaticVariable" im obigen Codebeispiel. Solche Variablen können unsere üblichen, aber informellen Benennungsgewohnheiten sehr verwirrend machen. Wenn wir zum Beispiel sagen " . " meinen wir normalerweise " lokal begrenzte, automatisch zugewiesene Variable "und wenn wir global sagen, meinen wir normalerweise " statisch zugewiesene Variable mit globalem Geltungsbereich ". Leider, wenn es um Dinge geht wie " statisch zugewiesene Variablen mit Dateispezifikation "Viele Leute sagen einfach ... " Was? ".
Einige der Syntaxentscheidungen in C/C++ verschärfen dieses Problem noch. So denken viele Leute, dass globale Variablen aufgrund der unten dargestellten Syntax nicht "statisch" sind.
int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation
int main() {return 0;}
Beachten Sie, dass das Schlüsselwort "static" in der obigen Deklaration verhindert, dass var2 einen globalen Geltungsbereich hat. Dennoch hat das globale var1 eine statische Zuweisung. Das ist nicht intuitiv! Aus diesem Grund versuche ich, bei der Beschreibung des Geltungsbereichs nie das Wort "static" zu verwenden und stattdessen etwas wie "file" oder "file limited" zu sagen. Viele Leute verwenden jedoch den Ausdruck "static" oder "static scope", um eine Variable zu beschreiben, auf die nur von einer einzigen Codedatei aus zugegriffen werden kann. Im Zusammenhang mit der Lebensdauer bedeutet "statisch" immer bedeutet, dass die Variable beim Programmstart zugewiesen und beim Beenden des Programms wieder freigegeben wird.
Manche Leute halten diese Konzepte für C/C++-spezifisch. Das sind sie aber nicht. Das folgende Python-Beispiel veranschaulicht beispielsweise alle drei Arten der Zuweisung (in interpretierten Sprachen sind einige subtile Unterschiede möglich, auf die ich hier nicht eingehen werde).
from datetime import datetime
class Animal:
_FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated
def PetAnimal(self):
curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)
class Cat(Animal):
_FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's
class Dog(Animal):
_FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!
if __name__ == "__main__":
whiskers = Cat() # Dynamically allocated
fido = Dog() # Dynamically allocated
rinTinTin = Dog() # Dynamically allocated
whiskers.PetAnimal()
fido.PetAnimal()
rinTinTin.PetAnimal()
Dog._FavoriteFood = 'milkbones'
whiskers.PetAnimal()
fido.PetAnimal()
rinTinTin.PetAnimal()
# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones
5 Stimmen
@mattshane Die Definitionen von Stack und Heap hängen überhaupt nicht von Wert- und Referenztypen ab. Mit anderen Worten, der Stack und Heap können vollständig definiert werden, auch wenn Wert- und Referenztypen niemals existiert hätten. Darüber hinaus ist der Stack nur ein Implementierungsdetail beim Verständnis von Wert- und Referenztypen. Laut Eric Lippert: Der Stack ist ein Implementierungsdetail, Teil Eins.
248 Stimmen
Eine wirklich gute Erklärung finden Sie hier Was ist der Unterschied zwischen einem Stapel und einem Heap?
19 Stimmen
Auch (wirklich) gut: codeproject.com/Articles/76153/… (der Stack-/Heap-Teil)
21 Stimmen
youtube.com/watch?v=clOUdVDDzIM&spfreload=5
5 Stimmen
Verwandte, siehe Stack Clash. Die Stack Clash-Beseitigungen betrafen einige Aspekte von Systemvariablen und Verhaltensweisen wie
rlimit_stack
. Siehe auch Red Hat Problem 14632411 Stimmen
Nicht klar in den Antworten: Für eine Laufzeitumgebung für eine Programmiersprache (z.B. .NET) gibt es pro Thread einen Stack, um Methodenaufrufe/lokale Variablen zu verwalten, und nur einen gemeinsamen Heap für alle Prozesse der Laufzeitumgebung. Der Heap wird vom Garbage Collector überwacht. Die Speicherbereiche der Laufzeitumgebung (Stacks/Heap) sind Teil des zusammenhängenden virtuellen Speichers, der vom Betriebssystem den Prozessen zugewiesen wird (der selbst von physischen RAM-Blöcken in keiner bestimmten Reihenfolge versorgt wird), auf Anforderung der Prozesse. Die Verwirrung um "Stacks" liegt an der Existenz vieler "Stack"-Arten in einem Computer, die nicht mit den Laufzeitumgebungs-Stacks zusammenhängen. Ein "Stack" ist einfach eine LIFO-Speicherstruktur.
0 Stimmen
Wenn Sie eine Simulation sehen möchten, wie der Stapel und der Heap während der Ausführung eines C-Programms aussehen, versuchen Sie C Tutor.
0 Stimmen
Du solltest definitiv dieses Video sehen, das alle oben genannten Antworten klärt youtube.com/watch?v=7O4JPdKjc30