什么存储在堆上以及存储在堆栈中的内容?

Amo*_*kar 21 c c++ java memory-management

任何人都可以用C,C++和Java来清楚地解释.什么都在堆栈上,什么都在堆上,什么时候分配完成.

我所知道的,

每个函数调用的基元,指针或引用变量的所有局部变量都在新的堆栈帧上.

用new或malloc创建的任何东西都在堆上.

我对一些事感到困惑.

作为在堆上创建的对象的成员的引用/原语是否也存储在堆上?

那些在每个帧中递归创建的方法的本地成员呢?它们都在堆栈上,如果是,则在运行时分配堆栈内存吗?对于文字,它们是代码段的一部分吗?那么C中的全局变量,C++/Java中的静态和C中的静态.

San*_*raj 35

记忆中程序的结构

以下是加载到内存中时任何程序的基本结构.

 +--------------------------+
 |                          |
 |      command line        |
 |        arguments         |
 |    (argc and argv[])     |
 |                          |
 +--------------------------+
 | Stack                    |
 | (grows-downwards)        |
 |                          |
 |                          |
 |                          |
 |         F R E E          |
 |        S P A C E         |
 |                          |
 |                          |
 |                          |
 |                          |
 |     (grows upwards) Heap |
 +--------------------------+
 |                          |
 |    Initialized data      |
 |         segment          |
 |                          |
 +--------------------------+
 |                          |
 |     Initialized to       |
 |        Zero (BSS)        |
 |                          |
 +--------------------------+
 |                          |
 |      Program Code        |
 |                          |
 +--------------------------+
Run Code Online (Sandbox Code Playgroud)

几点需要注意:

  • 数据段
    • 初始化数据段(由程序员初始化为显式初始化程序)
    • 未初始化的数据段(初始化为零数据段 - BSS [带符号的块开始])
  • 代码段
  • 堆栈和堆区域

数据段

数据段包含由包含初始化值的用户显式初始化的全局和静态数据.

数据段的另一部分称为BSS(因为旧的IBM系统将该段初始化为零).它是内存的一部分,OS将内存块初始化为零.这就是未初始化的全局数据和静态获取默认值为零的方式.该区域是固定的,具有静态尺寸.

基于显式初始化,数据区域被分成两个区域,因为要初始化的变量可以逐个初始化.但是,未初始化的变量不需要用0逐个显式初始化.而不是那样,初始化变量的工作留给操作系统.这种批量初始化可以大大减少加载可执行文件所需的时间.

大多数情况下,数据段的布局是在底层操作系统的控制下,仍有一些加载器对用户进行部分控制.此信息可用于嵌入式系统等应用程序.

可以使用代码中的指针来寻址和访问此区域.自动变量在每次需要时初始化变量时都会产生开销,并且需要代码来进行初始化.但是,数据区中的变量没有这样的运行时重载,因为初始化只执行一次,而且在加载时也是如此.

代码段

程序代码是可执行代码可用于执行的代码区域.这个区域也是固定的.这只能被函数指针访问,而不能被其他数据指针访问.此处要注意的另一个重要信息是系统可能将此区域视为只读存储区域,并且在此区域中写入的任何尝试都会导致未定义的行为.

常量字符串可以放在代码或数据区域中,这取决于实现.

写入代码区域的尝试导致未定义的行为.例如(我将仅提供C基于示例)以下代码可能导致运行时错误甚至导致系统崩溃.

int main()
{
    static int i;
    strcpy((char *)main,"something");
    printf("%s",main);
    if(i++==0)
    main();
}
Run Code Online (Sandbox Code Playgroud)

堆栈和堆区域

为了执行,程序使用两个主要部分,堆栈和堆.堆栈帧是在堆栈中为函数和堆创建的,用于动态内存分配.堆栈和堆是未初始化的区域.因此,无论在内存中发生什么,都会成为在该空间中创建的对象的初始(垃圾)值.

让我们看一个示例程序来显示哪些变量存储在哪里,

int initToZero1;
static float initToZero2;
FILE * initToZero3; 
// all are stored in initialized to zero segment(BSS)

double intitialized1 = 20.0;
// stored in initialized data segment

int main()
{
    size_t (*fp)(const char *) = strlen;
    // fp is an auto variable that is allocated in stack
    // but it points to code area where code of strlen() is stored

    char *dynamic = (char *)malloc(100);
    // dynamic memory allocation, done in heap

    int stringLength;
    // this is an auto variable that is allocated in stack

    static int initToZero4; 
    // stored in BSS

    static int initialized2 = 10; 
    // stored in initialized data segment   

    strcpy(dynamic,”something”);    
    // function call, uses stack

    stringLength = fp(dynamic); 
    // again a function call 
}
Run Code Online (Sandbox Code Playgroud)

或者考虑一个更复杂的例子,

// command line arguments may be stored in a separate area  
int main(int numOfArgs, char *arguments[])
{ 
    static int i;   
    // stored in BSS 

    int (*fp)(int,char **) = main;  
    // points to code segment 

    static char *str[] = {"thisFileName","arg1", "arg2",0};
    // stored in initialized data segment

    while(*arguments)
        printf("\n %s",*arguments++);

    if(!i++)
        fp(3,str);
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!


kub*_*uba 5

在C/C++中:局部变量在当前堆栈帧(属于当前函数)上分配.如果静态分配对象,则在堆栈上分配整个对象,包括其所有成员变量.使用递归时,每次调用函数都会创建一个新的堆栈帧,并在堆栈上分配所有局部变量.堆栈通常具有固定大小,并且该值通常在编译/链接期间写入可执行二进制头中.但是,这非常适合操作系统和平台,某些操作系统可能会在需要时动态增加堆栈.因为堆栈的大小通常是有限的,所以当您使用深度递归时,或者有时甚至在静态分配大型对象时没有递归时,可能会耗尽堆栈.

堆通常被视为无限空间(仅受可用物理/虚拟内存的限制),您可以使用malloc/new(以及其他堆分配函数)在堆上分配对象.在堆上创建对象时,将在其中创建其所有成员变量.您应该将对象视为连续的内存区域(此区域包含成员变量和指向虚方法表的指针),无论它在何处分配.

文字,常量和其他"固定"的东西通常被编译/链接到二进制文件中作为另一个段,所以它实际上并不是代码段.通常,您无法在运行时从此段中分配或释放任何内容.然而,这也是特定于平台的,它可能在不同平台上的工作方式不同(例如,iOS Obj-C代码有很多常量引用直接插入代码段,在函数之间).