C堆栈变量是否反向存储?
Are C stack variables stored in reverse?
我想了解 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 个字节,因为这将读取 调用者的 堆栈中 int y
之前的 4 个字节。
它没有打印出 152。奇怪的是,当我看接下来的 4 个字节时:
int *_prev_int = (int *) ((long unsigned int) i + sizeof(int)) ;
现在它可以工作了,打印调用者堆栈中 x
中的任何内容。为什么 x
的地址比 y
低?堆栈变量是否倒置存储?
堆栈组织完全未指定并且特定于实现。实际上,它取决于很多编译器(甚至是它的版本)和优化标志。
有些变量甚至不在堆栈上(例如,因为它们只是保存在某些寄存器中,或者因为编译器优化了它们 - 例如,通过内联、常量折叠等)。
顺便说一句,你可以有一些不使用任何堆栈的假设 C 实现(即使我不能说出这样的实现)。
要了解有关堆栈的更多信息:
阅读 call stacks, tail calls, threads, and on continuations
上的维基页面
熟悉你电脑的architecture & instruction set (e.g. x86) & ABI,然后...
请您的编译器显示汇编代码 and/or 一些中间编译器表示。如果使用 GCC, compile some simple code with gcc -S -fverbose-asm
(to get assembler code foo.s
when compiling foo.c
) and try several optimization levels (at least -O0
, -O1
, -O2
....). Try also the -fdump-tree-all
option (it dumps hundred of files showing some internal representations of the compiler for your source code). Notice that GCC also provides return address builtins
阅读 Appel 在 garbage collection can be faster than stack allocation, and understand garbage collection techniques (since they often need to inspect and possibly change some pointers inside call stack frames). To know more about GC, read the GC handbook 上的旧论文。
遗憾的是,我不知道可以在语言级别访问调用堆栈的低级语言(如 C、D、Rust、C++、Go 等)。这就是为什么为 C 编写垃圾收集器很困难(因为 GC-s 需要扫描调用堆栈指针)...但是请参阅 Boehm's conservative GC 以获得非常实用和务实的解决方案。
现在几乎所有的处理器架构都支持堆栈操作指令(例如ARM中的LDM、STM指令)。编译器在这些实现堆栈的帮助下。在大多数情况下,当数据被压入堆栈时,堆栈指针会递减(向下增长)并在数据从堆栈弹出时递增。
所以这取决于处理器架构和编译器如何实现堆栈。
取决于编译器和平台。同样的事情可以用不止一种方式完成,只要它由一个程序一致地完成(在这种情况下,编译器翻译成汇编,即机器代码)并且平台支持它(好的编译器会尝试优化汇编以获得“最”)。
免费书籍 初学者逆向工程(理解汇编语言) 深入了解 c 语言幕后的内容、编译程序时发生的情况以及它们发生的原因的一个很好的来源。 =16=],丹尼斯·尤里切夫,the latest version can be found at his site。
我想了解 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 个字节,因为这将读取 调用者的 堆栈中 int y
之前的 4 个字节。
它没有打印出 152。奇怪的是,当我看接下来的 4 个字节时:
int *_prev_int = (int *) ((long unsigned int) i + sizeof(int)) ;
现在它可以工作了,打印调用者堆栈中 x
中的任何内容。为什么 x
的地址比 y
低?堆栈变量是否倒置存储?
堆栈组织完全未指定并且特定于实现。实际上,它取决于很多编译器(甚至是它的版本)和优化标志。
有些变量甚至不在堆栈上(例如,因为它们只是保存在某些寄存器中,或者因为编译器优化了它们 - 例如,通过内联、常量折叠等)。
顺便说一句,你可以有一些不使用任何堆栈的假设 C 实现(即使我不能说出这样的实现)。
要了解有关堆栈的更多信息:
阅读 call stacks, tail calls, threads, and on continuations
上的维基页面
熟悉你电脑的architecture & instruction set (e.g. x86) & ABI,然后...
请您的编译器显示汇编代码 and/or 一些中间编译器表示。如果使用 GCC, compile some simple code with
gcc -S -fverbose-asm
(to get assembler codefoo.s
when compilingfoo.c
) and try several optimization levels (at least-O0
,-O1
,-O2
....). Try also the-fdump-tree-all
option (it dumps hundred of files showing some internal representations of the compiler for your source code). Notice that GCC also provides return address builtins阅读 Appel 在 garbage collection can be faster than stack allocation, and understand garbage collection techniques (since they often need to inspect and possibly change some pointers inside call stack frames). To know more about GC, read the GC handbook 上的旧论文。
遗憾的是,我不知道可以在语言级别访问调用堆栈的低级语言(如 C、D、Rust、C++、Go 等)。这就是为什么为 C 编写垃圾收集器很困难(因为 GC-s 需要扫描调用堆栈指针)...但是请参阅 Boehm's conservative GC 以获得非常实用和务实的解决方案。
现在几乎所有的处理器架构都支持堆栈操作指令(例如ARM中的LDM、STM指令)。编译器在这些实现堆栈的帮助下。在大多数情况下,当数据被压入堆栈时,堆栈指针会递减(向下增长)并在数据从堆栈弹出时递增。
所以这取决于处理器架构和编译器如何实现堆栈。
取决于编译器和平台。同样的事情可以用不止一种方式完成,只要它由一个程序一致地完成(在这种情况下,编译器翻译成汇编,即机器代码)并且平台支持它(好的编译器会尝试优化汇编以获得“最”)。
免费书籍 初学者逆向工程(理解汇编语言) 深入了解 c 语言幕后的内容、编译程序时发生的情况以及它们发生的原因的一个很好的来源。 =16=],丹尼斯·尤里切夫,the latest version can be found at his site。