我写了这样的代码
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
堆栈是否会长大并堆积起来?
答案 0 :(得分:8)
要了解“ 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;
}
你必须这样做,因为大多数编译器在进入时,在所谓的“堆栈帧”中为函数调用全部分配所有堆栈空间,并在其中组织空间他们认为合适。因此,比较同一函数的两个局部变量的地址并不能告诉你任何有用的东西。您还必须注意编译此程序并关闭“内联”;如果允许编译器将stack_probe
合并到main
中,则它将再次成为一个堆栈帧,结果将毫无意义。 (有些编译器允许你逐个函数地控制内联,但据我所知,没有标准的方法可以做到这一点。)
此程序打印的数字是“未指定”的C标准 3 (这意味着“它将打印某些号码,但标准不要求它是任何特定的数字“)。但是,几乎所有计算机上你都可能会在今天开始使用它,它会打印一个负数,这意味着堆栈会向下增长。如果您设法在运行HP-UX的PA-RISC计算机上运行它(它可能甚至不能编译,不幸的是;我不记得HP-UX是否曾经拥有符合C99标准的库)它将打印一个正数,并且这意味着堆栈向上增长。
有计算机,其上由此程序打印的数字并不意味着什么,因为它们相当于“堆栈”不一定是连续的内存块。查找最简单版本的“拆分堆栈”。
顺便说一下,“堆”不一定会长大或。连续调用malloc
返回指针,彼此没有任何有意义的关系,总是。
1 可以有多个堆栈,例如在使用线程时。
2 有趣的事实:C标准中的“堆栈”一词出现无处。需要支持递归函数调用,但实现如何管理完全由实现完成。
3 此外,该程序是否将编译是实现定义的,因为不需要实现intptr_t
和uintptr_t
。但是,如果我没有使用这些类型,该程序将具有未定义的行为(“它允许执行任何操作,包括崩溃并删除所有代码”),因为您只允许当它们指向相同的数组时,取两个指针的差异,这些不是。
答案 1 :(得分:3)
标准未规定所有这些。该标准甚至没有提到堆栈和堆。这两个都是标准不需要的实现细节。
此外,您只有一个动态对象(malloc'ed对象),它将遵循数组的正常布局。因此,您无法说明堆在系统上的增长情况。如果你想试试看你的系统做了什么,你至少需要两个malloc'ed对象。
要打印指针,请使用:
printf("%p, %p\n", (void*)&a, (void*)&b);
答案 2 :(得分:1)
因为指针算术:
printf("%x, %x", &m[0], &m[1]);
(请注意,打印指针值需要%p
格式,其他格式可以“正常”但它们也可能会中断)
m[1]
的地址是m[0]
加sizeof(int)
的地址,不依赖于m
的分配位置(全局或自动)
在这里:
int a = 0;
int b = 0;
与结构成员相反,编译器可以在相对于彼此选择的任何地方找到自动变量。它可以交换它们,对具有较低对齐约束的那些进行分组等等......所以你在这里看一个实现细节。
答案 3 :(得分:0)
是的,几乎所有现代系统的堆栈都在增长。但是,1。这是一个实现细节,2是堆栈帧,而不是单个变量。
当你的函数被调用时,它会在堆栈上创建一个堆栈帧,它基本上是堆栈内存的一个区域,用于保存局部变量。每个函数都放在这个堆栈框架中,完全取决于编译器。
但是,如果您运行此代码:
void foo() {
int b;
printf("%p\n", (void*)&b);
}
int main() {
int a;
printf("%p\n", (void*)&a);
foo();
}
您会发现b
的地址小于a
的地址:当foo()
被调用时,它会创建一个新的堆栈帧,b
将会在这个堆栈框架内,它将在main()
分配的堆栈框架下面。
对于堆,没有任何方向的概念。当然,大多数分配器都会在分配内存方面表现出一些模式,但是当它们都重用free()
时的内存时,它们都会在某些时候打破它们的模式。
答案 4 :(得分:0)
正如许多人所指出的那样,C搁浅并没有具体说明这一点。
但是我想触及不同的方面,测试你做的是不足以确定堆正在成长 - 单词还是单词。
如下所示更新代码并检查结果;
#include <stdio.h>
#include <stdlib.h>
void test()
{
int a = 0;
int b = 0;
printf("4:%p, %p\n", (void *)&a,(void *)&b);
}
void main() {
int a = 0;
int b = 0;
int *m = malloc(2* sizeof(int));
int *n = malloc(2* sizeof(int));
printf("1:%p, %p\n",(void *)&a,(void *)&b);
printf("2:%p, %p\n",(void *)&m[0],(void *)&m[1]);
printf("3:%p, %p\n",(void *)&n[0],(void *)&n[1]);
test();
}
在我的系统上,我可以看到堆地址&#34;增加&#34;和堆栈地址&#34;减少&#34;
在您的代码中,您没有任何函数调用,因此堆栈帧只有一个。