繁体   English   中英

数组存储在内存中的什么位置?

[英]Where is an array stored in memory?

我试图了解如何在 C 程序中管理内存。 我知道内存中有以下段:

  1. 初始化数据段
  2. BSS
  3. 代码

现在考虑以下程序:

#include <stdio.h>

int main(){
    int arr[4] = {1,2,3,4};
    int x = 10;

    printf("Hello World!");
}

在上面的程序中, arr 和 x 都是在 main 函数中局部声明的。 我认为这意味着它们都将在函数堆栈上分配空间。 但是,当我在 linux 上运行 size 命令时,我发现数组实际上是在数据段中分配了空间。

我在网上搜索过这个,但发现了相互矛盾的信息。 一些答案说所有本地声明的变量都应该进入堆栈,而其他人说数组应该进入堆。 我认为如果我使用 malloc 动态分配内存,数组会进入堆,在这个例子中不是这种情况。

我在网上搜索过这个,但发现了相互矛盾的信息。

请不要随意阅读博客之类的内容,它们通常包含不好的信息。 在 Stack Overflow 上,错误的信息往往会被低估,或者至少通常会有评论指出不准确和谬误。

在上面的程序中, arr 和 x 都是在 main 函数中局部声明的。 我认为这意味着它们都将在函数堆栈上分配空间。

C 标准没有指定变量对象的内存应该如何分配。 它只指定对象具有存储持续时间,它定义了变量对象的生命周期。

  • 静态,从程序开始到结束都有生命周期
  • 自动,它将具有包含声明(或复合文字)的最内层块{ ... }的生命周期,直到块结束
  • 线程本地,它将具有线程的生命周期
  • 分配的对象,它们将从malloc/calloc/realloc/aligned_alloc一直malloc/calloc/realloc/aligned_alloc到相应的free/realloc

除此之外,C 标准规定,在其生命周期内,对象将

  • 为它保留内存
  • 并有一个常量地址(您可以使用&操作符观察)

现在,除此之外,还有所谓的as-if规则,即只要程序的外部行为相同,编译器就可以生成任何程序代码,外部行为意味着输入、输出、访问易失性对象等。

程序中的变量具有自动存储期,这意味着每次进入main函数时,您都会拥有具有新生命周期的对象,直到main函数结束。 通常这意味着它们将存储在堆栈中,因为它将以最小的开销很好地处理分配和释放。 但是您的程序具有相同的外部行为

#include <stdio.h>

int main(void) {
    printf("Hello World!");
}

这意味着编译器可以完全消除这两个变量,并且不为其保留任何空间。

现在,如果您打印变量的地址

#include <stdio.h>

int main(void) {
    int arr[4] = {1,2,3,4};
    int x = 10;

    printf("Hello World! %p, %p\n", (void *)arr, (void *)&x);
}

因为变量的地址被占用并用于输出,C 无法优化它们。 他们现在在堆栈中吗? 好吧,C标准不说了。 它们至少需要从main开始到结束都有生命周期 - 但是 C 编译器可以决定为它们使用堆栈,因为该程序的外部行为与

#include <stdio.h>

static int arr[4] = {1,2,3,4};
static int x = 10;
    
int main(void) {
    printf("Hello World! %p, %p\n", (void *)arr, (void *)&x);
}

这会将这些变量放在静态数据段中; 当然地址是不同的,但再次C没有给出关于特定的对象位于内存中的任何保证,只是他们将有地址。

但是,当我在 linux 上运行 size 命令时,我发现数组实际上是在数据段中分配了空间。

我想你误解了你所看到的。

C 标准对此没有任何说明。 它只说arr具有自动存储持续时间。 但是,大多数(如果不是全部)系统会将xarr保存在堆栈中。

试试这个代码:

#include<stdio.h>

int main(){
    int arr[4] = {1,2,3,4};
    int x = 10;
    static int i = 0;

    printf("Hello World! arr is here %p and x is here %p\n", (void*)arr, (void*)&x);
    ++i;
    if (i < 3) main();
    
    return 0;
}

可能的输出:

Hello World! arr is here 0x7ffcdaba4170 and x is here 0x7ffcdaba416c
Hello World! arr is here 0x7ffcdaba4140 and x is here 0x7ffcdaba413c
Hello World! arr is here 0x7ffcdaba4110 and x is here 0x7ffcdaba410c

即使这不是一个可靠的证据,它也强烈表明系统正在使用一个堆栈,并且堆栈向低地址增长,并且arrx都存储在该堆栈上。

顺便说一句:打印堆栈指针不能以可移植的方式完成,但这是一个很好的阅读: 打印堆栈指针的值

C中程序的存储工作如下:

全局变量 -------> 数据

静态变量 -------> 数据

常量数据类型 -----> 代码和/或数据。 当常量本身存储在数据段中,并且对它的引用将嵌入代码中时,请考虑字符串文字

局部变量(在函数中声明和定义)--------> 堆栈

在主函数中声明和定义的变量 -----> 堆也栈

指针(例如:char *arr, int *arr) -------> 堆数据或堆栈,具体取决于上下文。 C 允许您声明全局或静态指针,在这种情况下,指针本身将在数据段中结束。

动态分配空间(使用 malloc、calloc、realloc)--------> 堆栈堆

值得一提的是,“堆栈”的官方名称是“自动存储类”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM