数组存储在内存中的啥位置?
Posted
技术标签:
【中文标题】数组存储在内存中的啥位置?【英文标题】:Where is an array stored in memory?数组存储在内存中的什么位置? 【发布时间】:2021-01-17 19:46:18 【问题描述】:我试图了解在 C 程序中如何管理内存。我知道内存中有以下几个段:
-
初始化数据段
BSS
堆栈
堆
代码
现在考虑以下程序:
#include <stdio.h>
int main()
int arr[4] = 1,2,3,4;
int x = 10;
printf("Hello World!");
在上面的程序中,arr 和 x 都是在 main 函数中本地声明的。我认为这意味着它们都将在函数堆栈上分配空间。 但是,当我在 linux 上运行 size 命令时,我发现数组实际上是在数据段中分配空间的。
我在网上搜索过这个,但发现了相互矛盾的信息。一些答案说所有本地声明的变量都应该进入堆栈,而另一些答案则说数组应该进入堆。如果我使用 malloc 动态分配内存,我认为数组会进入堆,但在本例中并非如此。
【问题讨论】:
请注意,您的初始化程序1,2,3,4
将存储在数据部分的程序映像中。在运行时,如果您的程序甚至创建了数组变量(因为正如其他人所说,优化器可以删除任何无效的内容),它将在堆栈上为数组创建空间,然后将初始化程序复制到其中。或者,如果使用优化器,它可能会删除初始化程序并将其替换为内联寄存器集指令。
【参考方案1】:
我已经在网上搜索过,但发现了相互矛盾的信息。
请不要随意阅读博客之类的,它们通常包含不良信息。在 Stack Overflow 上,错误信息往往会被否决,或者至少通常会让 cmets 指出不准确和谬误。
在上面的程序中,arr 和 x 都是在 main 函数中本地声明的。我认为这意味着它们都将在函数堆栈上分配空间。
C 标准没有指定应该如何为变量对象分配内存。它仅指定对象具有存储持续时间,它定义了变量对象
...
的生命周期,直到块结束
thread-local,具有线程的生命周期
分配的对象,从malloc/calloc/realloc/aligned_alloc
直到对应的free/realloc
都处于活动状态。
除此之外,C 标准规定对象在其生命周期内将
为其保留内存 并且有一个常量地址(您可以使用&
运算符观察)
现在,除此之外,还有一条the so-called as-if 规则说只要程序的外部行为相同,编译器就可以生成任何程序代码,外部行为意味着输入、输出、访问 volatile对象等等。
你程序中的变量有自动存储期限,这意味着每次你进入main
函数你都会有新的个对象新的生命周期 直到 main
函数结束。通常这意味着它们将被存储在 stack 中,因为它将以最小的开销很好地处理分配和释放。但是您的程序具有与
#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 也不保证特定对象在内存中的位置,只是它们会有地址。
【讨论】:
【参考方案2】:但是,当我在 linux 上运行 size 命令时,我发现数组实际上是在数据段中分配空间。
我认为你误解了你所看到的。
C 标准对此没有任何说明。它只说arr
具有自动存储期限。但是,大多数(如果不是全部)系统会将x
和arr
保存在堆栈中。
试试这个代码:
#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
即使这不是一个可靠的证据,它也强烈表明系统正在使用一个堆栈并且堆栈向低地址增长并且arr
和x
都存储在该堆栈上。
顺便说一句:打印堆栈指针不能以可移植的方式完成,但这是一个很好的阅读:Print out value of stack pointer
【讨论】:
我明白你的意思。让我感到困惑的是,当我使用 size a.out 命令查看内存分配时。如果我不包含数组并且只有一个var,例如int i for ex,那么内存有bss 8和数据600。但是当我将arr包含到程序中时,数据段变成了608。现在,我试过了添加更多变量,例如 int a、b、c 并给它们一些值(或者它们可能最终在 BSS 中!)但数据段没有改变!它仍然是 608,如果我删除阵列,它会再次变为 600!所以这让我很困惑。 @JANVISHARMA 您可能忘记了本地数组的初始化值(即1,2,3,4
)也可能存储在程序中。顺便提一句。我只是在Compiler Explorer 上摆弄你的例子。在那里你可以看到在sub rsp, 40
中考虑了本地数组的空间(向下移动堆栈指针以为本地变量分配空间)但是初始化只是用mov
s 完成 - 它没有常量数据(虽然有比如我的printf()
s 的常量字符串。
@JANVISHARMA 而且,如果您喜欢使用我的链接示例,请尝试将编译器选项从 -O2
更改为 -O0
... ;-)【参考方案3】:
C 中程序的存储工作如下:
全局变量------->数据
静态变量------->数据
常量数据类型 -----> 代码和/或数据。当常量本身存储在数据段中并且对它的引用将嵌入到代码中时,考虑使用字符串文字
局部变量(在函数中声明和定义)-------->栈
在主函数中声明和定义的变量----->堆也是栈
pointers(ex: char *arr, int *arr) -------> 堆数据或堆栈,取决于上下文。 C 允许您声明一个全局或静态指针,在这种情况下,指针本身将在数据段中结束。
动态分配的空间(使用malloc、calloc、realloc)-------->栈堆
值得一提的是,“栈”被官方称为“自动存储类”。
【讨论】:
以上是关于数组存储在内存中的啥位置?的主要内容,如果未能解决你的问题,请参考以下文章
React Native - AsyncStorage 和状态存储在硬件内存中的啥位置?