doc*_*_id 6 c stack pointers memory-address
我试图理解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);
}
Run Code Online (Sandbox Code Playgroud)
看看我在做什么?假设sizeof(int)是4:我在传递的指针后面看了4个字节,因为它会int y在调用者堆栈中的位置之前读取4个字节.
它没有打印152.奇怪的是当我看下4个字节时:
int *_prev_int = (int *) ((long unsigned int) i + sizeof(int)) ;
Run Code Online (Sandbox Code Playgroud)
现在它可以工作,打印x调用者堆栈内的任何内容.为什么x地址低于y?堆栈变量是否颠倒存储?
Bas*_*tch 10
堆栈组织完全未指定,并且是特定于实现的.实际上,它取决于很多编译器(甚至是它的版本)和优化标志.
有些变量甚至不在堆栈上(例如,因为它们只是保存在某些寄存器中,或者因为编译器对它们进行了优化 - 例如内联,常量折叠等).
顺便说一下,你可以有一些假设的C实现,它不使用任何堆栈(即使我不能命名这样的实现).
要了解有关堆栈的更多信息:
阅读有关调用堆栈,尾调用,线程和延续的wikipage
请求编译器显示汇编代码和/或一些中间编译器表示.如果使用GCC编译一些简单的代码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需要扫描调用堆栈指针)......但是看看Boehm的保守GC是一个非常实用和实用的解决方案.