我想了解一下C语言是如何在栈上分配内存的,我一直以为栈上的变量可以像结构体成员变量那样描述,它们在栈内占据连续、连续的字节块。我一直以为栈上的变量可以像结构体成员变量那样描述,它们在栈中占据连续的、连续的字节块。为了帮助说明我在某处发现的这个问题,我创建了这个小程序,它再现了这个现象。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void function(int *i) {
int *_prev_int = (int *) ((long unsigned int) i - sizeof(int)) ;
printf("%d\n", *_prev_int );
}
void main(void)
{
int x = 152;
int y = 234;
function(&y);
}
看到我在做什么了吗?假设 sizeof(int)
是4:我在寻找传递的指针后面的4个字节,因为那会读出前面的4个字节,在那里 int y
在 来电者 栈。
它没有打印152。奇怪的是,当我看了下4个字节。
int *_prev_int = (int *) ((long unsigned int) i + sizeof(int)) ;
现在,它的工作,打印无论在 x
在调用者的栈内。为什么要这样做?x
的地址低于 y
? 堆栈变量是颠倒存储吗?
堆栈组织完全 不详 并且是 具体实施. 实际上,这在很大程度上取决于编译器(甚至是它的版本)和优化标志。
有些变量甚至不在堆栈中(例如,因为它们被保存在一些寄存器中,或者因为编译器对它们进行了优化--例如,通过内联,常量折叠等)。
另外,你可以有一些假设的C语言实现,它不使用任何堆栈(即使我不能说出这样的实现)。
要了解更多关于堆栈的信息。
要求你的编译器显示汇编器代码和一些中间的编译器表示。如果使用 海湾合作委员会编译一些简单的代码,用 gcc -S -fverbose-asm
(获取汇编器代码 foo.s
编译时 foo.c
),并尝试多个优化级别(至少 -O0
, -O1
, -O2
....). 也可以试试 -fdump-tree-all
选项(它转储了上百个文件,显示了编译器对你的源代码的一些内部表示)。注意,GCC还提供了 返回地址内置
阅读Appel的旧文 垃圾回收可以比堆栈分配更快,并了解 垃圾回收 技术(因为他们经常需要检查并可能改变调用栈框架内的一些指针)。要了解更多关于GC的信息,请阅读 GC手册.
遗憾的是,我不知道任何一种低级语言(如C、D、Rust、C++、Go......)可以在语言层访问调用栈。这就是为什么为C语言编写垃圾收集器是很困难的(因为GC-s需要扫描调用栈指针)...... 但请看 玻姆的保守派 为一个非常实用的解决方案。
现在几乎所有的处理器架构都支持堆栈操作指令(如ARM中的LDM、STM指令)。编译器借助这些实现堆栈。在大多数情况下,当数据被推入堆栈时,堆栈指针会递减(向下生长),而当数据从堆栈弹出时,堆栈指针会递增。
所以这取决于处理器架构和编译器如何实现堆栈。