何时/在哪里分配本地数组?

时间:2019-04-19 21:39:15

标签: c arrays gcc memory

https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-and-C.html描述了局部变量的自动分配。我知道局部变量通常在堆栈上分配。我可以想象如何在堆栈上分配int;只是推动其价值。但是如何分配数组?

例如,如果您声明一个数组char str[10];,那么这10个字节的空间是否放在堆栈上,还是在其他地方分配了空间,只有str指针被压入了堆栈?如果是后者,那么在哪里分配了10个字节的空间?

此外,何时精确分配局部变量(包括数组)?我通常将堆分配称为“动态分配”,这意味着自动变量不是动态分配的。但是可以在控制流结构和函数体内声明自动变量,因此编译器可能在运行时之前无法确切知道自动变量将占用多少空间。因此,还必须动态分配自动变量,对吧?

编辑:我想强调一下这个问题的前半部分。我最想了解何时和何处分配本地数组的空间。在堆栈上?其他地方?

编辑2:当我最初为这个问题添加C ++标记时,我犯了一个错误。我只想问一下 C 语言及其实现。如有任何困惑,我深表歉意。

3 个答案:

答案 0 :(得分:2)

在C 2018标准中,第6.2.4节第6和第7段告诉我们具有自动存储持续时间的对象的生存期。第6段涵盖了不是可变长度数组的此类对象:

  

…其生存期从进入与之关联的块开始一直到该块的执行以任何方式结束。 (进入封闭的块或调用函数会暂停执行,但不会结束当前块的执行。)如果递归地输入该块,则每次都会创建该对象的新实例。

因此,如果我们有以下代码:

{
    PointA;
    int x = 3;
    PointB;
}

然后,一旦执行到达x,即进入PointA的生存期,则x就存在于C模型中。但是,尽管x中已经存在PointA,但其值是不确定的。初始化仅在达到定义时发生。

第7段介绍了可变长度数组:

  

…它的生存期从对象的声明开始,直到程序执行离开声明的范围为止。

因此,如果我们有以下代码:

{
    PointA;
    int x[n]; // n is some variable.
    PointB;
}

然后x上的PointA不存在。其生存期始于达到int x[n];

请记住,这种存在仅就C语言的抽象计算模型而言。只要可观察到的结果(例如程序的输出)相同,编译器就可以优化代码。因此,输入该块时,由编译器生成的实际代码可能不会创建x。 (它可能根本不会创建x;可以完全对其进行优化。)

答案 1 :(得分:0)

您要查询的内容取决于语言实现(编译器)。为了回答您的问题,以下是(简化的概述)编译器通常对编译后的语言(如C / C ++)执行的操作:

当编译器完成对函数的解析时,它将保留该函数中声明的所有局部变量的符号表,甚至包括在函数的指令流期间“语法上声明”的那些局部变量(如局部循环变量)。稍后,当需要生成最终(汇编)代码时,它将生成必要的指令以为所有局部变量压入(或仅移动堆栈指针)足够的空间。因此,例如,当循环开始执行时,不会分配局部循环变量。而是在执行包含循环的函数的开始时分配它们。编译器还添加了指令,以在从函数返回之前删除此分配的堆栈空间。

因此,在这种(常见)情况下,自动变量(如char数组)完全分配在堆栈上。

[EDIT]可变长度数组(在C99之前)

上面的讨论是针对具有这样的长度的数组,这些长度在编译时是已知的:

void f () {
    char n[10];
    ....
}

如果我们使用C语言术语(在C99之前),则将可变长度数组(其长度在编译时未知,而在运行时未知)声明为这样的指针:

void f() {
    char *n;
    ... //array is later allocated using some kind of memory allocation construct
}

实际上,这只是声明一个指向数组的指针。指针大小是编译器已知的。因此,如上所述,无论运行时数组的大小如何,编译器都将能够为栈上的指针保留必要的存储空间(只是指针,而不是真正的数组)。当执行到达分配数组的行(例如,使用malloc)时,将在堆上动态分配数组存储,其地址存储在本地自动变量n中。在没有垃圾回收的语言中,这需要手动从堆中释放(释放)保留的存储(即,当不再需要数组时,程序员应在程序中添加一条指令来执行此操作)。如前所述,对于常量大小的数组(分配在堆栈上),这不是必需的,因为编译器会在从函数返回之前删除堆栈帧。

[EDIT2]

C99可变长度数组不能在堆栈上声明。编译器必须在生成的机器代码中添加一些代码,以便在运行时处理其动态创建和销毁。

答案 2 :(得分:0)

  

例如,如果您声明一个数组char str [10] ;,那10个字节的空间是否会放在堆栈上,还是在其他地方分配了,只有str指针才被压入堆栈?如果是后者,那么在哪里分配了10个字节的空间?

通常,与其他任何局部变量一样,数组的存储在堆栈上分配。这是编译器和特定于目标的。即使在x86_64机器上,可能也不会在堆栈上分配40亿字节的数组。我期望以下之一:编译错误,链接错误,运行时错误,或者它以某种方式起作用。在最后一种选择中,它可以调用new[]malloc()并将指针指向数组而不是数组。

请注意,数组的分配及其指针是同一回事,因此您添加的 分配在其他地方,并且只有str指针 可能表示混淆。发生分配,并且其名称不是独立数据。