C 为什么堆栈和堆都会增长?

C 为什么堆栈和堆都会增长?,c,memory,C,Memory,我写这样的代码 int a = 0; int b = 0; int *m = (int *)malloc(2* sizeof(int)); printf("%x, %x\n", &a, &b); printf("%x, %x", &m[0], &m[1]); 并得到结果: 46372e18, 46372e1c d062ec20, d062ec24 这不是因为堆栈变小而堆高吗?是因为指针算法: printf("%x, %x", &m[0], &

我写这样的代码

int a = 0;
int b = 0;
int *m = (int *)malloc(2* sizeof(int));

printf("%x, %x\n", &a, &b);
printf("%x, %x", &m[0], &m[1]);
并得到结果:

46372e18, 46372e1c
d062ec20, d062ec24

这不是因为堆栈变小而堆高吗?

是因为指针算法:

printf("%x, %x", &m[0], &m[1]);
(请注意,打印指针值需要
%p
格式,其他格式可以“工作”,但也可以中断)

m[1]
的地址是
m[0]
加上
sizeof(int)
,这不取决于
m
的分配位置(全局或自动)

在这里:

int a = 0;
int b = 0;

与结构成员不同的是,编译器可以将自动变量定位在它选择的相对位置。它可以交换它们,将具有较低对齐约束的对象分组,等等。。。因此,您在这里看到了一个实现细节。

因为指针算法:

printf("%x, %x", &m[0], &m[1]);
(请注意,打印指针值需要
%p
格式,其他格式可以“工作”,但也可以中断)

m[1]
的地址是
m[0]
加上
sizeof(int)
,这不取决于
m
的分配位置(全局或自动)

在这里:

int a = 0;
int b = 0;

与结构成员不同的是,编译器可以将自动变量定位在它选择的相对位置。它可以交换它们,将具有较低对齐约束的对象分组,等等。。。因此,您在这里看到了一个实现细节。

所有这些都不是标准规定的。标准甚至没有提到堆栈和堆。这两个都是标准不需要的实现细节

此外,您只有一个动态对象(malloc'ed对象),它将遵循数组的正常布局。因此,你不能说任何关于堆在你的系统上是如何增长的。如果您想尝试查看系统的功能,则至少需要两个malloc’ed对象

要打印指针,请使用:

printf("%p, %p\n", (void*)&a, (void*)&b);

所有这些都不是本标准规定的。标准甚至没有提到堆栈和堆。这两个都是标准不需要的实现细节

此外,您只有一个动态对象(malloc'ed对象),它将遵循数组的正常布局。因此,你不能说任何关于堆在你的系统上是如何增长的。如果您想尝试查看系统的功能,则至少需要两个malloc’ed对象

要打印指针,请使用:

printf("%p, %p\n", (void*)&a, (void*)&b);
要了解“1”堆栈“2”的增长方式,必须至少进行一次函数调用。例如,类似这样的事情:

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>

static ptrdiff_t
stack_probe(uintptr_t stack_addr_from_main)
{
    int var;
    uintptr_t stack_addr_from_me = (uintptr_t)&var;

    return ((intptr_t) stack_addr_from_me) - 
           ((intptr_t) stack_addr_from_main);
}

int
main(void)
{
    int var;
    uintptr_t stack_addr_from_main = (uintptr_t)&var;
    ptrdiff_t stack_delta = stack_probe(stack_addr_from_main);
    printf("Stack offset from one function call = %td\n", stack_delta);
    return 0;
}
#包括
#包括
#包括
静态ptrdiff\t
堆栈探测器(从主堆栈添加到主堆栈)
{
int-var;
uintpttr_t stack_addr_from_me=(uintpttr_t)&var;
返回((intptr_t)stack_addr_from_me)——
((intptr_t)stack_addr_from_main);
}
int
主(空)
{
int-var;
uintpttr_t stack_addr_from_main=(uintpttr_t)&var;
ptrdiff_t stack_delta=stack_探针(stack_addr_from_main);
printf(“一个函数调用的堆栈偏移量=%td\n”,堆栈增量);
返回0;
}
您必须这样做,因为大多数编译器一次分配函数调用的所有堆栈空间,在输入时,在所谓的“堆栈框架”中,并根据需要组织其中的空间。因此,将两个局部变量的地址与同一个函数进行比较并不能告诉您任何有用的信息。您还必须注意在关闭“内联”的情况下编译此程序;如果允许编译器将
stack\u probe
合并到
main
中,那么它将再次成为一个堆栈帧,结果将毫无意义。(有些编译器允许您逐个函数地控制内联,但据我所知,没有标准的方法来实现这一点。)

此程序打印的数字由C标准3“未指定”(即“它将打印一些数字,但标准不要求它是任何特定的数字”)。然而,在几乎所有你今天可能接触到的计算机上,它都会打印一个负数,这意味着堆栈向下增长。如果您在运行HP-UX的PA-RISC机器上运行它(不幸的是,它甚至可能无法编译;我不记得HP-UX是否有符合C99的库),它将打印一个正数,这意味着堆栈向上增长

有些计算机上这个程序打印的数字没有任何意义,因为它们的“堆栈”不一定是连续的内存块。查找“拆分堆栈”中最简单的版本

顺便说一句,“堆”不一定会增长或下降。对
malloc
的连续调用始终返回彼此之间没有意义关系的指针


1可以有多个堆栈,例如在使用线程时

2有趣的事实:“堆栈”一词在C标准中没有出现。有一个支持递归函数调用的需求,但是实现如何管理它完全取决于实现

3此外,该程序是否编译由实现定义,因为该实现不需要提供
intptr\u t
uintptr\u t
。但如果我没有使用这些类型,程序将有未定义的行为(“它可以做任何事情,包括崩溃和删除所有代码”),因为只有当两个指针指向同一数组时,才允许使用它们之间的差异,而这两个指针不允许。要了解“1”堆栈“2的增长方式,您必须至少进行一次函数调用。例如,类似这样的事情:

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>

static ptrdiff_t
stack_probe(uintptr_t stack_addr_from_main)
{
    int var;
    uintptr_t stack_addr_from_me = (uintptr_t)&var;

    return ((intptr_t) stack_addr_from_me) - 
           ((intptr_t) stack_addr_from_main);
}

int
main(void)
{
    int var;
    uintptr_t stack_addr_from_main = (uintptr_t)&var;
    ptrdiff_t stack_delta = stack_probe(stack_addr_from_main);
    printf("Stack offset from one function call = %td\n", stack_delta);
    return 0;
}
#包括
#包括
#包括
静态ptrdiff\t
堆栈探测器(从主堆栈添加到主堆栈)
{
int-var;
uintpttr_t stack_addr_from_me=(uintpttr_t)&var;
返回((intptr_t)stack_addr_from_me)——
((intptr_t)stack_addr_from_main);
}
int
主(空)
{
int-var;
uintpttr_t stack_addr_from_main=(uintpttr_t)&var;
ptrdiff_t stack_delta=stack_探针(stack_addr_from_main);
printf(“堆栈