堆叠和堆肥是什么?它们实际上位于计算机的内存中在哪里?它们在多大程度上被操作时间或语言控制?它们的范围是什么?它们的大小是什么?它们大小是什么?一个的大小是什么使一个更快?
当前回答
其他答案只是避免解释静态分配意味着什么。 所以我会解释三种主要分配形式,以及它们通常与下面的堆积、堆叠和数据段的关系。 我还会在 c/c++ 和 python 中展示一些例子,以帮助人们理解。
静态( 静态分配) 变量没有在堆叠上分配。 不要假设- 许多人只是因为“ 静态” 听起来像“ 堆叠 ” 。 它们实际上既不存在于堆叠中, 也不存在于堆叠中。 它们属于所谓的数据段 。
然而,一般而言,最好考虑“范围”和“终生”,而不是“堆积”和“堆积”。
范围指代码中哪些部分可以访问变量。 我们一般认为本地范围(只能通过当前函数访问)与全球范围(任何地方都可以访问)不同,尽管范围可能变得更加复杂。
当一个变量在程序执行期间被分配和交易时, 其使用寿命值是指变量在程序执行期间被分配和交易。 我们通常会想到静态分配( 在整个程序期间会持续不变, 使得它可用于在多个函数调用中存储相同的信息), 而会想到自动分配( 仅在对函数的单次调用中持续不变, 使得它可用于存储仅在您函数期间使用、 一旦完成即可丢弃的信息) 相对于动态
尽管大多数编译者和口译员在使用堆叠、堆堆堆等方面也采取了类似的做法,但只要行为正确,编译者有时会打破这些公约。例如,由于优化,本地变量可能只存在于登记册中,或者完全删除,即使大多数本地变量存在于堆叠中。正如在几个评论中所指出的,你可以自由实施一个甚至不使用堆叠或堆叠的编译者,但有些则可以使用
i 将提供一些简单的附加说明的 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.
一个特别令人印象深刻的例子说明为什么区分寿命和范围很重要,那就是变量可以具有局部范围,但固定寿命——例如,在上文的代码样本中“某些局部可变性”。这些变量可以使我们共同但非正式的命名习惯非常混乱。例如,我们说“本地”通常是指“局部范围自动分配变量”,而我们说“全球范围”通常是指“全球范围静态分配变量”。 不幸的是,当我们说“本地”时,我们通常是指“全球范围的静态分配变量”。
C/c+++中的一些语法选择加剧了这一问题,例如许多人认为全球变数不是“静态”的,
int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation
int main() {return 0;}
请注意, 在以上声明中加上关键词“ 静态” 会使 var2 无法具有全球范围。 然而, 全球 val1 具有静态分布 。 这不是直观的 。 因此, 我试图在描述范围时永远不要使用“静态” 一词, 而不是说“ 文件” 或“ 文件有限” 的范围。 但是许多人使用“静态” 或“ 静态范围” 来描述一个变量, 只能从一个代码文件中访问 。 在生命周期中, “ 静态” 总是意指从一个代码文件中访问的变量 。
有些人认为这些概念是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
其他回答
许多答案作为概念是正确的,但我们必须指出,硬件(即微处理器)需要堆叠,才能调用子例程(用组装语言调用)。 (oop guys will call it ways)
在您保存返回地址的堆栈上, 并拨“ 推 / 重 ” Pop 由硬件直接管理 。
您可以使用堆叠来设定通过参数。 即使比使用登记册要慢( 微处理器大师会说, 或者好的 1980 年代生物书...) )
没有堆叠, 没有微处理器是行不通的。 (我们无法想象一个程序, 即使是在组装语言中, 没有子例程/功能) 没有它能工作的堆肥( 组装语言程序可以工作, 因为堆肥是一个 os 概念, 作为 malloc, 是一个 os/lib调用 ) 。
堆栈使用速度更快, 以 :
硬体是硬件,甚至推/棒也非常有效。 中转器需要输入内核模式,使用锁/石墨(或其他同步原始元素)执行某些代码,并管理一些跟踪分配情况所需的结构。
堆叠 :
堆叠上创建的变量将超出范围,自动交易。 与堆叠上的变量相比, 分配速度要快得多。 使用实际的堆叠数据结构执行。 存储本地数据、 返回地址、 参数通过时使用的方法。 当堆叠过多使用时, 可能会出现堆叠溢出( 大多来自无限或过深的循环, 非常大的配置) 。 堆叠上创建的数据可以在没有指针的情况下使用 。
堆积 :
在 c+++ 中, 堆积上的变量必须手动销毁, 并且绝不会掉出范围 。 数据通过删除、 删除或自由解脱而解脱。 与堆叠上的变量相比, 分配速度较慢。 需要时使用来分配块块数据供程序使用。 当有大量分配和交易位置时, 可能会发生碎裂 。 在 c+++ 或 c 中, 堆积上生成的数据将被指向指针, 并分别与新数据或中位相分配 。
例如:
int foo()
{
char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
bool b = true; // Allocated on the stack.
if(b)
{
//Create 500 bytes on the stack
char buffer[500];
//Create 500 bytes on the heap
pBuffer = new char[500];
}//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;
当调用函数时,当调用该函数的参数加上一些其他间接费用时,会将数据堆放到堆栈中。一些信息(例如返回何处)也存储在那里。当您在函数中声明变量时,该变量也会被分配到堆栈中。
分配堆栈非常简单, 因为您总是在分配的反向顺序中进行排列。 在输入函数时会添加堆叠材料, 当退出时相应的数据会被删除。 这意味着您往往会留在堆栈的狭小区域, 除非您调用许多函数来调用其他函数( 或者创建循环解决方案 ) 。
堆积堆是一个通用名称, 用于您将创建的数据放在哪里 。 如果您不知道您的程序将创建多少飞船, 您可能会使用新的( 或商场或等效的) 操作器来创建每艘飞船 。 此分配将会停留一段时间, 因此我们很可能释放的东西 以不同于我们创建的顺序 。
因此,堆积要复杂得多,因为最终会出现一些未使用的记忆区域,这些区域与块间断 — — 内存会变得支离破碎。 找到您需要的大小的自由记忆是一个困难的问题。 这就是为什么应该避免堆积( 尽管它仍然经常被使用 ) 。
执行堆叠和堆叠通常要到运行时间 / os. 通常游戏和其他功能对于性能至关重要的应用程序会创建自己的内存解决方案,从堆叠中抓取一大块内存,然后在内部将内存分离出来,以避免依赖 os 来进行内存。
只有当你的记忆用法与常规有很大不同时, 也就是在游戏中, 在一个巨大的操作中加载一个水平, 并且可以在另一个巨大的操作中将整个批量扔掉时, 这才是实际的。
内存中的物理位置比你想的要少, 这是因为一种叫做虚拟内存的技术, 它使得您的程序认为您可以进入某个地址, 物理数据在其他地方( 即使是在硬盘上!) 。 您获得的堆叠地址随着调用树越深, 顺序越大。 堆积的地址是无法预测的( 具体化) , 坦率地说并不重要 。
它们(在真实的计算机记忆中)在哪里和什么?
回答:两者都是在山上。
暂不考虑 :
公牛就像一张书桌, hdds/sds(永久存储)就像一张书架。 要阅读任何东西, 您必须在桌子上打开一本书, 您只能在桌子上打开尽可能多的书。 要拿到一本书, 您可以从书架上拉出来, 并在桌子上打开。 返回一本书, 您关闭桌子上的书架, 然后把它还给书架 。
堆叠和堆肥是我们给两种方式的汇编者提供的名称,它们将不同种类的数据储存在同一地点(即用大号堆放)。
它们的范围是什么?它们各自的大小由什么决定?一个的大小由什么决定?一个的大小由什么决定?
答复:
堆栈是静态(固定大小) a. 编译器读取您代码中使用的变量类型。i. 它为这些变量分配了固定的内存量。 ii. 此内存的大小无法增长。 b. 内存是毗连的( 单块) , 因此存取有时比堆叠的更快。 放在堆叠上的一个物体在堆叠大小超过其大小的运行时会增加内存, 导致堆叠溢出错误, 堆积是动态( 变化大小) 数据 a. amoun 。
暂不考虑 :
堆叠和堆肥主要不是为了提高速度而引入的;它们被引入是为了处理内存溢出。 有关堆叠与堆积之间的第一个问题应该是:是否会出现内存溢出。 如果一个物体的大小打算增长到一个未知的数量(如链接的清单或其成员可以持有任意数量数据的对象),则将其放入堆肥。 尽可能使用 c++ 标准库(stl) 容器矢量、 地图和列表,因为它们是内存和列表。
在运行您的代码后, 如果您发现代码运行速度慢得令人无法接受, 然后回去重新构思您的代码, 并检查它是否能够更高效地编程。 它可能会发现问题与堆叠或堆积完全无关( 比如使用迭代算法而不是循环算法, 看看 i/ o / vs. cpu- imbound 任务, 也许添加多读或多处理 ) 。
因为程序的速度可能与堆叠或堆叠上分配的物品无关。
在多大程度上它们受国家或语言的运行时间控制?
答复:
堆叠大小由编译者在编译时确定。 堆积大小在运行时会变化。 (堆积在运行时与 os 一起工作,以分配内存 。)
暂不考虑 :
下面是控制和编译时间相对于运行时间操作的多一点。
每台计算机都有独特的指令集结构(isa),这是其硬件命令(例如“移动”、“跳”、“跳”、“添加”等)。
a os 只不过是一个资源管理者(控制如何/时间/和何处使用内存、处理器、装置和信息)。 os 的状态被称为光机,其余的命令被称为扩展机。 内核是扩展机的第一层。 它控制着诸如确定什么任务可以使用处理器(调度器)、多少内存或有多少硬件登记册可以分配给任务(调度器),以及任务的顺序。
堆栈是内存的一部分,可以通过若干关键组装语言指令来操作,例如“pop”(从堆叠中移动并返回一个值)和“push”(将一个值推到堆叠中),但也可以调用(调用子例程 - 将地址推到堆叠中)和返回( 从子例程返回 - 将堆叠的地址从堆叠中跳出并跳到堆叠中)。这是堆叠指针登记册下方的内存区域,可以根据需要设置。堆叠也用于 pa 。
堆积是操作系统向一个应用程序提供的内存的一部分, 通常通过像马洛克这样的音响。 在现代套管上, 这个内存是一组只有呼叫程序才能进入的页面 。
堆叠的大小在运行时确定, 一般在程序启动后不会增长。 在 c 程序中, 堆叠需要足够大, 以保持每个函数中所有声明的变量。 堆叠会按需要动态增长, 但 巨头最终会发出呼唤( 它会增加的堆积量通常超过 merloc 要求的值, 这样至少有些未来的中枢不需要返回内核来获取更多的内核内存 。 此行为通常可以自定义 )
因为您在启动程序前已经分配了堆叠, 所以在您使用堆叠之前, 您从不需要在使用堆叠前使用滚动, 所以在实际操作中这是一个小的优势。 实际上, 很难预测什么是快速的,什么是缓慢的, 在拥有虚拟内存子系统的现代操作系统中, 因为这些页面是如何被安装的, 在哪里存储的, 是一个执行细节 。
推荐文章
- 在Objective-C中@property保留,赋值,复制,非原子
- Python内存泄漏
- 什么是ORM,它是如何工作的,我应该如何使用它?
- 我能在服务器端应用程序(PHP、Ruby、Python等)上读取URL的哈希部分吗?
- 多少个参数是太多?
- 对于不可变集合上的非突变“add”方法,最好的名称是什么?
- Go中结构体的堆栈与堆分配,以及它们与垃圾收集的关系
- brk()系统调用做什么?
- foo到底是什么意思?
- foreach和map有区别吗?
- 为什么我应该使用Deque而不是Stack?
- 设计模式:工厂vs工厂方法vs抽象工厂
- 什么是栈展开?
- 为什么处理排序数组比未排序数组慢?
- 构造函数何时抛出异常是正确的?