C程序的内存分配

11

程序

#include<stdio.h>
int a=10;
void main()
{
    int i=0;
    printf("global = %p, local = %p\n",&a,&i);
    main();
}

输出

mohanraj@ltsp63:~/Advanced_Unix/Chapter7$ ./a.out
global = 0x804a014, local = 0xbfff983c
global = 0x804a014, local = 0xbfff980c
.
.
.
global = 0x804a014, local = 0xbf7fac9c
global = 0x804a014, local = 0xbf7fac6c
global = 0x804a014, local = 0xbf7fac3c
Segmentation fault (core dumped)
mohanraj@ltsp63:~/Advanced_Unix/Chapter7$

上述程序出现分段错误。因为主函数调用了自身,导致递归。以下是C程序的内存分配。

内存分配

          __________________                        __________________  
          |                |                        |                | 
          |      stack     |                        |     Main       |
          |  ↓             |                        |----------------|
          ------------------                        |     Main       | 
          |                |                        |----------------|
          |   <Un Allocated|                        |     Main       |
          |       space>   |                        |----------------|
          ------------------                        |     Main       |
          |                |                        |----------------|
          |    ↑           |                        |     Main       |
          |       Heap     |                        |----------------|
          |                |                        |     Main       |
          |                |                        |----------------|
          __________________                        |////////////////| ---> Collision occurs. So, Segmentation fault Occurs.  
          |                |                        |________________|
          |       data     |                        |       data     |
          __________________                        |________________|
          |       text     |                        |       text     |
          __________________                        |________________|
               Figure(a)                               Figure(b)

所以,我期望像图(b)中展示的那样,主函数被递归调用。如果它到达了数据段,就会发生碰撞。 如果发生了碰撞,就没有更多的空间来分配给主函数。因此,它会得到分段错误。因此,使用上面的程序进行实验。 在该程序中,全局变量'a'的地址为"0x804a014"。每次调用主函数时,本地变量"i"都会被声明。所以, 我期望,在分段错误之前,'i'的地址接近于'a'的地址。但是,这两个地址非常不同。那么这里发生了什么。

为什么在分段错误发生时,'a'和'i'的地址不在同一范围内?那么,如何交叉检查主函数是否达到了堆栈大小并被溢出?


5
暂停程序,并查看 /proc/pid/maps - 其中还有其他东西,比如库。此外,堆栈大小实际上是有限制的(ulimit)。 - Anya Shenanigans
6
你的堆栈大小可能受到"ulimit"的限制,一般为8MB。即使在32位系统上,在这种情况下你也永远不会让堆栈与数据段重叠;即使它们之间没有许多库。我试图指导你理解一个真实进程的地址映射与你展示的简单映射不同。 - Anya Shenanigans
所以,堆栈大小是固定的。它不会增长到数据段。这是正确的吗? - mohangraj
1
尝试类似于“ulimit -s 65000000; ./a.out”的命令,程序将会运行更长时间。 - ysdx
1
@TobySpeight:在 C++ *3.6.1 Main function [basic.start.main] §3 中,明确禁止调用 main 函数:该函数不得在程序内部使用。但我找不到 C 规范中的相应说明,因此看起来允许这样做——尽管这种做法极为不常见…… - Serge Ballesta
显示剩余6条评论
4个回答

1

您的模式是一个概念性模型或可能的实现。但例如,多线程程序将每个线程拥有一个堆栈和一个单一堆,这并不真正适合您简化的模式。

所需的只是系统允许递归,这意味着每个函数的新调用都会获得本地变量的私有副本。其余部分取决于实现。

最近的系统使用页面分配,并且进程通常会获得一组页面段,但它们不一定是连续的,您可以在它们之间有“空洞”,其中任何访问都会收到SIGSEGV(段违规)信号。

TL / DR:您的程序更有可能收到SIGSEGV信号,而不是动态变量的地址达到静态变量的地址-您应该找到一个旧的MS / DOS盒子来展示这种行为...


0

0

当你写下变量"i"被声明时,你写的是正确的内容,但是这个变量并没有像全局变量a一样被声明。

i是栈分配的,而栈有自己的大小。

ulimit可以改变这个限制。

你想要看到的,全局变量和局部变量地址分配之间的冲突,是不可能发生的。


0

你所拥有的内存模型是一个巨大的简化,仅用于向学生教授内存基础知识,以免在开始时过多地介绍细节。就像我们从自然数的加法开始进行数学教育一样。这是一个良好的基础,但如果您仅使用它来理解现实,那么它将无法帮助您走得更远。

这个模型至少已经不准确25-30年了,不能用来预测实际程序行为。堆和栈之间可能存在数十个,甚至数千个其他内存映射(共享库、大型malloc、文件的mmap等)。“堆”概念非常棘手,因为通常它不过是地址空间中任何匿名内存动态分配总和,用于操作系统事先不知道的目的。

在您的示例中发生的情况是您遇到了堆栈资源限制(请参见ulimit -s)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接