malloc() |堆栈和堆位置的内存地址长度的差异| C 编程

Posted

技术标签:

【中文标题】malloc() |堆栈和堆位置的内存地址长度的差异| C 编程【英文标题】:malloc() | difference in memory address length of stack and heap locations | C programming 【发布时间】:2017-11-13 00:13:53 【问题描述】:

我正在学习c程序的内存管理。我有一个很好的疑问。 (Ubuntu 操作系统)

我的疑问:

我想知道位于堆栈和堆内的数据的地址。但是当我尝试打印这些地址时,我发现地址的长度是不同的! 这里的问题是为什么它显示的堆栈地址比堆地址长?

我知道的:

每个进程的堆栈内存是固定的,小于堆内存。 malloc() 在堆上分配内存 局部变量入栈

我把我的演示代码放在这里,这样你就可以很好地回答我的疑问了。

#include <stdio.h>
#include <stdlib.h>

int main()

    int *ptr; // goes on stack

    ptr = (int *)malloc(sizeof(int));
    *ptr = 10; // 10 is stored on heap
    printf("%p : heap address\n",ptr);
    printf("%p:  stack address\n",&ptr);
    return 0;


输出: 我在终端中得到了以下输出

0x1ea2010 : heap address
0x7ffda62cb3c0:  stack address

所以现在你可能明白我在问什么了。为什么栈地址比堆地址长?堆是一个很大的内存池,所以显然它应该有更多的长度。

如果堆栈和堆分配是在同一个内存块中完成的(根据现代操作系统..我在某处读过这个),那么它也应该具有相同的长度。

好的。请帮我把我的记忆概念弄得一清二楚。

注意:如果我的疑问非常简单或愚蠢,那么至少也请让我知道我的演示代码中的内存分配是如何完成的,以及不同长度地址背后的魔法。

感谢您阅读此类帖子。回答愉快!

【问题讨论】:

这些只是您要打印的虚拟地址。由于动态链接,无法确定内存的实际物理地址。如果我错了,请有人纠正我。 来自 C 标准关于%p 转换类型说明符:“指针的值以实现定义的方式转换为打印字符序列。 ”。你应该区分这个输出和内部表示。请提供对要求堆栈地址不得大于堆地址的标准的参考(或根本需要堆栈或堆)。 “如果堆栈和堆分配在同一个内存块中完成(根据现代操作系统..我在某处读过这个)” - 不要相信一些晦涩的网站写的一切。这没有任何意义。您应该要求作者澄清。 @Parth 通常堆栈向零地址的方向增长。这解释了 0x7ffda62cb3c0 这么大的值。例如尝试定义一个包含许多元素的本地数组,你会发现它的地址小于你得到的值。 @Parth 请在下面查看我的答案,看看是否有帮助。 【参考方案1】:

鉴于您运行的是 Ubuntu,我假设您运行的是 x86 或 x86-64 平台。假设这是真的,您的程序布局如下所示:

              +-----------------------------+
High Address: |   Command-line arguments    |
              |  and environment variables  |
              +-----------------------------+
              |            Stack            |
              |              |              |
              |              V              |
              |                             |
              |              ^              |
              |              |              |
              |            Heap             |
              +-----------------------------+
              |     Uninitialized Data      |
              +-----------------------------+
              |      Initialized Data       |
              +-----------------------------+
              |        Program Text         |
 Low Address: |       (machine code)        |
              +-----------------------------+

堆栈从一个高地址开始并“向下”增长(朝向递减地址),而堆从一个相当低的地址开始并“向上”增长(朝向递增地址)。 %p 转换说明符不打印地址值中的前导零;如果是这样,您的地址将如下所示

0x0000000001ea2010: heap address
0x00007ffda62cb3c0: stack address

两个地址的长度确实相同,只是没有显示前导零。

【讨论】:

我理解你想要表达的意思。是的,长度与我们允许前导零相同。【参考方案2】:

您在 printf 中的格式字符串指定跳过前导零,这是默认设置。您需要添加所需长度的打印地址,如 %016p、%016x 或 %016X(如果您需要大写十六进制字符)。

正如您正确假设的那样,所有指针的长度必须相同。

【讨论】:

【参考方案3】:

您似乎在使用 64 位地址,这意味着它们打印为 最多 16 个十六进制字符。您应该在左侧用零填充所有地址以达到 16 个字符。

0x0000000001ea2010: heap address
0x00007ffda62cb3c0: stack address

堆和栈都存在于相同的虚拟 2^64 字节空间中。

【讨论】:

【参考方案4】:

没有标准。

但是,通常情况下,堆向上增长而堆栈向下增长。因此,从逻辑上讲,在开始时,Heap 的大小会更小,而 Stack 的大小会更大。

存在这种实现的原因是,它使程序具有静态与动态内存重叠的机会较小。

上面有人提到了虚拟地址和物理地址。从上下文来看,进程中的每一块内存都是一个虚拟地址,因此,试图解释 BUDDY 算法充其量是一种冗余,最坏的情况是无关紧要。

【讨论】:

【参考方案5】:

想象你住在一条长街上。你住在街道的南端,房子地址是 1、2、3。

想象街道向北延伸一英里。想象一下,在街道的北端,地址是 998、999、1000。

想象一下,到目前为止,只开发了街道的南北两端。从地址20到地址990不过是未开发的空地。

但在街道的尽头有很多活动。新买家购买 20 到 30 地块并开始在其上建造房屋。

与此同时,在街道的北端,没有那么多活动。看起来 989 和 990 批次已经售出,并且正在那里建造一些东西。

那么,哪里有更大的未开发地块“池”?在街道的北端还是南端?

到目前为止,街道南端的房屋(1-20 栋,并且还在增长)比街道北端的房子多(990-1000 栋)。到目前为止,街道南端的增长速度也更快。 (南端有 10 个在建地段,而北端有 2 个。)

然而,街道北端的地址(以 9 开​​头的 3 位或 4 位)大于南端的地址(2 位)。这是什么意思? (答案:这并不意味着什么。)

【讨论】:

以上是关于malloc() |堆栈和堆位置的内存地址长度的差异| C 编程的主要内容,如果未能解决你的问题,请参考以下文章

关于new的堆栈问题

堆栈和堆内存的大小[重复]

编译器如何在内存中布局代码

在java中初始化数组时的堆栈和堆内存[重复]

.net 中的堆栈和堆内存分配

堆栈和堆的内容和位置是什么?