所有的正在运行的程序都在内存条(RAM)中运行,其中内存的最小单元是字节(最小可寻址单位)。因此,所有概念上的堆栈等,应当建立在内存条这一硬件之上进行讨论。
内存条(DRAM)出厂时,只是一大块均匀的、无结构的存储单元。它并没有被明确的画好哪里是队列,哪里是堆,哪里是栈。就像一张白纸,上面没有预先画好的格子。因此,堆栈并没有真真正正的物理硬件结构。但应当注意,当操作系统启动一个进程时,它会临时在进程的虚拟地址空间中进行逻辑划分。
例如,在操作系统中可能有如下脚本用于分配内存:

process_address_space = {
    .text   = 0x00400000-0x00401000,   
    .data   = 0x00600000-0x00602000, 
    .heap   = 0x00602000-0x0...,       // 这里规定了逻辑上的堆曲
    .stack  = 0x7ffffffdd000-0x7ffffffff000  // 这里分配逻辑上的栈区
};

当线程实际开始执行时,操作系统并不会立即把整个栈区都映射到物理内存,而是只映射最顶部的几页物理内存到栈的虚拟地址范围。随着栈的增长(函数调用加深),如果触及了尚未映射的虚拟地址,会触发页面错误,操作系统才会动态分配新的物理内存页并映射上去。
因此,并不存在名为堆栈的硬件结构,堆栈只是操作系统给进行的逻辑划分。
在C++等编程语言中,对于std::stack这样的栈类型,我将与其他数据类型的存储方法在这里一同进行梳理。
这里的关键在于,变量存储位置由它的定义方式决定,而不是数据类型。
在内存中,有三大主要部分,分别为堆、栈、静态/全局区。如下图。

不同位置声明的变量将在不同的位置分配内存。例如:

void example() { 
    int a = 10; // a作为局部变量,存放在栈上
    static int b = 20; // b作为静态对象,存放在静态/全局区 
    int* c = new int(30); // 指针c在栈上,它指向的值(30)在堆上 
} 
int global = 40; // global是全局变量,存放在静态/全局区

对于C++容器(std::vector, std::stack, std::string等)应当注意,容器对象本身和它管理的数据通常在不同区域。也就是说,容器对象与其数据是分开管理的。

#include <stack>
#include <vector>

void containerExample() {
    //如果是局部容器:
    std::stack<int> s1;
    std::vector<int> v1(100);
    
    // s1 是stack对象的本身,在栈上
    // v1 是vector对象的本身,也在栈上
    // 但是请留意:
    // s1 内部存储的int元素在堆上,因为std::stack默认用std::deque,而deque从堆分配
    // v1 内部的100个int元素也在堆上,因为vector从堆分配内存
    
    //如果是动态分配的容器:
    std::stack<int>* s2 = new std::stack<int>();
    
    // s2 这个指针在栈上,因为该指针本身作为对象本身。
    // s2 指向的std::stack对象在堆上,因为std::stack是栈对象本身。
    // s2内部存储的int元素在堆上
}

以上是本文的所有内容。希望对大家有所帮助。如果有需要,我将继续更新【栈帧】的相关内容。
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com