Чтобы уточнить, этот ответ содержит неверную информацию ( Томас исправил свой ответ после комментариев, круто :)). Другие ответы просто не объясняют, что означает статическое распределение. Итак, ниже я объясню три основных формы распределения и их связь с кучей, стеком и сегментом данных. Я также покажу некоторые примеры на C / C ++ и Python, чтобы помочь людям понять.
«Статические» (статически распределенные AKA) переменные не размещаются в стеке. Не думайте так - многие люди делают только потому, что «статический» очень похож на «стек». Они на самом деле не существуют ни в стеке, ни в куче. Это часть того, что называется сегмент данных .
Однако, как правило, лучше рассматривать « scope » и « life », а не «stack» и «heap».
Область действия относится к тому, какие части кода могут обращаться к переменной. Обычно мы думаем о локальной области действия (доступ к которой может получить только текущая функция) по сравнению с глобальной области действия (можно получить доступ в любом месте), хотя область действия может стать намного более сложной.
Время жизни - это когда переменная выделяется и освобождается во время выполнения программы. Обычно мы думаем о статическом распределении (переменная будет сохраняться в течение всей продолжительности программы, что делает ее полезной для хранения одной и той же информации при нескольких вызовах функций) по сравнению с автоматическим распределением (переменная сохраняется только во время одного вызова функции, что делает ее полезной для хранения информации, которая используется только во время вашей функции и может быть отброшена, когда вы закончите) по сравнению с динамическим распределением (переменные, продолжительность которых определяется во время выполнения, вместо время компиляции как статическое или автоматическое).
Хотя большинство компиляторов и интерпретаторов реализуют это поведение аналогично с точки зрения использования стеков, куч и т. Д., Компилятор может иногда нарушать эти соглашения, если он хочет, пока поведение корректно. Например, из-за оптимизации локальная переменная может существовать только в регистре или может быть полностью удалена, даже если большинство локальных переменных существует в стеке. Как было отмечено в нескольких комментариях, вы можете свободно реализовывать компилятор, который даже не использует стек или кучу, а вместо этого использует некоторые другие механизмы хранения (редко делается, поскольку стеки и кучи отлично подходят для этого).
Я приведу простой аннотированный C-код для иллюстрации всего этого. Лучший способ научиться - это запускать программу под отладчиком и наблюдать за ее поведением. Если вы предпочитаете читать Python, перейдите к концу ответа:)
// 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.
Особенно яркий пример того, почему важно различать время жизни и область видимости, состоит в том, что переменная может иметь локальную область видимости, но статическое время жизни - например, «someLocalStaticVariable» в приведенном выше примере кода. Такие переменные могут сделать наши общие, но неформальные привычки именования очень запутанными. Например, когда мы говорим « local », мы обычно имеем в виду « локально распределенную автоматически назначаемую переменную », а когда мы говорим «global», мы обычно имеем в виду « глобально ограниченную статически распределенную переменную ». К сожалению, когда речь заходит о таких вещах, как " статически распределенные переменные в пределах файла ", многие просто говорят ... " да ??? ".
Некоторые из вариантов синтаксиса в C / C ++ усугубляют эту проблему - например, многие люди думают, что глобальные переменные не являются «статическими» из-за синтаксиса, показанного ниже.
int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation
int main() {return 0;}
Обратите внимание, что добавление ключевого слова "static" в объявлении выше препятствует тому, чтобы var2 имел глобальную область видимости. Тем не менее, глобальное var1 имеет статическое размещение. Это не интуитивно понятно! По этой причине я стараюсь никогда не использовать слово «статический» при описании области действия, а вместо этого говорю что-то вроде «файл» или «ограниченный файл» область действия. Однако многие люди используют фразу «статический» или «статический объем» для описания переменной, доступ к которой возможен только из одного файла кода. В контексте времени жизни «static» всегда означает, что переменная выделяется при запуске программы и освобождается при выходе из программы.
Некоторые люди считают эти понятия специфичными для C / C ++. Они не. Например, приведенный ниже пример Python иллюстрирует все три типа распределения (в интерпретируемых языках возможны некоторые тонкие различия, о которых я не буду здесь говорить).
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