• 什么是堆叠和堆叠?
  • 他们在电脑记忆中身处何方?
  • 它们在多大程度上受到操作系统或语言运行时间的控制?
  • 其范围是什么?
  • 是什么决定了它们的大小?
  • 是什么让一个更快?

当前回答

OK,OK, OK, OK, OK, OK, OK, OK, OK,OK, OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,OK,简言简简短地说,它们的意思是:已订购未排序...!

堆堆叠:在堆叠的物品中,事情会相互占据顶端, 意味着处理的速度会更快,效率会更高!

所以总是有一个索引来指向特定项目, 处理速度也会更快, 这些项目之间也有关系!

堆肥:没有订单,处理速度会放慢, 价值会混乱在一起,没有具体的订单或索引... ......有随机的,它们之间没有关系... 所以执行和使用时间可以不同...

我还创造了下面的图像,以显示他们可能长得如何:

enter image description here

其他回答

其他人对大中风的反应也很好, 所以我要讲一些细节。

  1. 堆放和堆放不需要是单数的 。 堆放和堆放的多处常见情况是, 您在一个过程中拥有多个线条。 在此情况下, 每个线条都有自己的堆放。 您也可以有多个堆放。 例如, 某些 DLL 配置可能导致不同堆放的 DLL 分配不同的 DLL , 这就是为什么释放不同图书馆分配的内存通常是一个坏主意 。

  2. 在 C 中,您可以通过使用单花,它分配在堆叠上,而不是 Alloc,它分配在堆肥上。这个记忆不会保存在您的返回语句中,但它对刮痕缓冲很有用。

  3. 在 Windows 上做一个不使用很多内容的大型临时缓冲区不是免费的。 这是因为编译器将生成一个堆叠探测器循环, 每次输入您的函数时都会被调用, 以确保堆叠存在( 因为 Windows在堆叠的末尾使用一个单个的守护页面来检测堆叠的生长需要。 如果您访问堆叠尾端的多页内存, 您将会崩溃 ) 。 例如 :

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}

其他答案只是避免解释静态分配意味着什么。 因此,我将解释三种主要分配形式,以及它们通常与下面的堆积、堆叠和数据段的关系。 我还将在C/C++和Python中展示一些例子,以帮助人们理解。

“ 静态” (AKA静态分配) 变量没有在堆叠上分配 。 不要假设- 许多人这样做, 只是因为“ 静态” 听起来像“ 堆叠 ” 。 它们实际上既不存在于堆叠中,也不存在于堆叠中。 它们属于所谓的“ 堆叠” 的一部分 。数据元数据段.

然而,一般而言最好考虑 " 。范围范围" 和 "寿命寿命而不是"堆"和"堆"

范围指代码中哪些部分可以访问变量。本地范围(只能由当前函数访问)和全球范围尽管范围可能变得更加复杂,但范围(无论何处都可以进入)仍会变得更加复杂。

寿命指变量在程序执行期间分配和交易的时间。通常我们想到的是静静分配(在程序的整个整个期间,将始终可变,因此有助于在多个函数调用中储存相同信息)相对于自动分配(只有在对函数的单一次呼叫中,可变性才能持续,使该函数可用于存储仅在您函数期间使用、一旦完成即可丢弃的信息)和动态分配(期限在运行时界定的可变数据,而不是静态或自动的时间。)

尽管大多数编译者和口译员在使用堆叠、堆肥等方面也采取了类似的做法,但只要行为正确,编译者有时会打破这些公约。例如,由于优化,本地变量可能只存在于一个登记册中,或者完全删除,即使大多数本地变量存在于堆叠中。正如在几个评论中指出的,您可以自由使用一个甚至不使用堆叠或堆积的编译者,而是使用其他一些存储机制(因为堆叠和堆积对这很重要,因为堆叠和堆积对这很重要 ) 。

我将提供一个简单的附加注释的 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++/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

我有些话要说,尽管主要要点已经涵盖在内。

堆堆叠

  • 非常快速的进入。
  • 存储在记录和档案管理中。
  • 函数调用与本地变量和已通过的函数参数一起装入这里 。
  • 当程序超出范围时,空间会自动释放。
  • 存储在相继内存中 。

堆肥

  • 与Stack相比,Stack的准入速度缓慢。
  • 存储在记录和档案管理中。
  • 动态创建的变量存储在这里, 以后需要使用后释放分配的内存 。
  • 存储到记忆分配的地方 指示器总是能存取

有趣的是:

  • 如果功能电话储存在堆积中,就会造成两点混乱:
    1. 由于在堆叠中相继存储, 执行速度会更快。 堆积中的堆积会导致大量时间消耗, 从而使整个程序的执行速度放慢 。
    2. 如果将功能储存在堆积中(指针指向的沉降器),就不可能返回调用地址(堆叠是由于内存的顺序存储而提供的) 。

您可以用堆叠做一些有趣的事情。 例如, 您的函数像单花(假设你能够通过有关其使用的大量警告) 这是一种专门用堆叠而不是堆叠来记忆的 中转器形式。

也就是说, 堆叠式的记忆错误是我经历过的最糟糕的记忆错误。 如果您使用堆积式内存, 并超越了分配区块的界限, 您就有一个很好的机会触发断段断层断层。 ( 不是100%: 您的区块可能附带地与先前分配的区块毗连 。 ) 但是由于堆叠上创建的变量总是相互连结, 边框中的写法可以改变另一个变量的值。 我了解到, 只要我感到我的程序不再遵守逻辑法则, 它可能就是缓冲溢出 。

(我将这一答案从另一个或多或少是这个问题的假象的问题移出。 )

您问题的答案是具体执行问题,可能因汇编者和处理结构而异。但这里是简单的解释。

  • 堆叠和堆叠都是从基本操作系统分配的记忆区(通常是虚拟内存,按要求绘制成物理内存)。
  • 在一个多轨环境中, 每条线将拥有自己的完全独立的堆叠, 但是它们会共享堆叠 。 同时访问必须控制在堆叠上, 无法在堆叠上 。

堆积物

  • 堆积中包含一个链接的旧区块和空空区块列表。newmalloc)通过在自由区块中创建一个合适的区块来满足。这需要更新堆积层上的区块清单。元数据信息堆积物层的区块 也常储存在堆积物层上 在一个小区域 就在每个块块的前面
  • 随着堆积增加,新区块往往从下层地址分配到更高的地址。堆肥内存区块的大小随内存分配而增大。如果堆积太小,无法分配,则从基本操作系统获取更多的内存,其内存量往往会增加。
  • 分配和分配许多小区块可能会让堆积物离开堆积物的状态下,在用过的区块之间有许许多多的小型自由区块。 分配大区块的请求可能会失败,因为没有一块自由区块能够满足分配要求,即使自由区块的组合体大小可能足够大。 这被称为“无自由区块 ” 。堆积碎裂.
  • 当使用过的自由区块旁边的块块在交易时,新的自由区块可以与邻近的自由区块合并,以创建一个更大的自由区块,有效地减少堆积的碎裂。

The heap

堆叠

  • 堆叠工作通常与一个名为CPU的特别登记簿密切配合进行。堆叠指针。最初,堆叠指针指向堆叠的顶部(堆叠上的最高地址)。
  • CPU有特别指示推推堆叠和弹出弹出从堆放堆放的堆放物中推进保存堆叠指针当前位置的值,并减少堆叠指针。 A弹出检索堆叠指针指向的值,然后增加堆叠指针(不要被以下事实混淆):添加堆叠的值减少堆叠指针和删除删除a 值增加数保存并检索的值是 CPU 登记册的值。
  • 如果函数有参数,则这些参数在调用到函数之前被推到堆栈上。然后,函数中的代码能够从当前的堆叠指针上导航堆栈以定位这些值。
  • 当函数被命名为 CPU 时, 函数会使用特殊指令来按当前指示指示器后,当函数返回时,旧的指令指针会从堆叠中跳下来,然后在调用该函数后,在代码中恢复执行。
  • 当输入一个函数时,会降低堆叠指针,以便在堆栈上为本地(自动)变量分配更多空间。如果函数有一个本地32位变量,则在堆栈上留出四个字节。当函数返回时,会将堆叠指针移回所分配的区域。
  • 括号函数调用功能像一个护符一样工作。 每一个新调用功能参数、 返回地址和本地变量空间, 以及这些变量激活记录可以堆放嵌套电话,函数返回时会以正确的方式卸载。
  • 由于堆叠是一个有限的内存块块, 您可以引起堆叠溢溢溢通过调用过多的嵌套函数和/ 或为本地变量分配过多的空间。 堆栈使用的记忆区域通常设置在堆栈底部( 最低地址) 下方的刻录将触发CPU的陷阱或例外。 此特殊条件随后会被运行时间捕获, 并转换成某种堆叠溢出例外 。

The stack

能否在堆叠上而不是堆叠上分配函数 ?

否,函数(即本地变量或自动变量)的激活记录被分配到堆叠上,不仅用于存储这些变量,还用于跟踪嵌套功能电话。

如何管理堆肥实际上要到运行时的环境。 C 使用mallocC++ 和C++ 用途new,但许多其他语言都有垃圾收集。

然而,堆叠是一个更低层次的特性,它与处理器结构紧密相连。 当没有足够的空间时堆积起来不会太难, 因为可以在处理堆积的图书馆电话中执行。 但是, 堆叠堆积起来往往是不可能的, 因为堆积溢出的时间太晚才被发现; 关闭行刑线是唯一可行的选择。