堆栈增长方向

7

从我之前的memmove问题中,我想知道如何找到堆栈增长的方向。

  void stackDirection(int* i)
  {

     int j;

     if(&j>i)
         cout<<"Stack is growing up \n"<<endl;
     else 
         cout<<"Stack is growing down \n"<<endl;


   }
  int main()
  {    
      int i=1;

      stackDirtection(&i);

 }

2
你的代码不起作用了,或者出了什么问题? - nothrow
问题在于我无法确定这段代码是否正确。 - brett
在我看来,一个更好的测试方法是依次调用alloca,因为栈指针在同一帧中,不会受到任何编译器优化的影响。 - Necrolis
如果你进行了一项实验,并获得了有效且可重复的结果... - Potatoswatter
最好在同一个函数中比较两个变量的地址。在主函数中,您可以创建j变量并比较地址。这应该会给出有效的结果。 - bjskishore123
@bjskishore123:同一函数中的两个变量不会给出答案。您只能测试变量在一个栈帧中的布局方式,而无法测试堆栈的增长方式。 - Mike Seymour
5个回答

14

堆栈可能不会向上或向下增长。

每个堆栈帧都可以在堆的任意点处分配。
实际上,在几个操作系统中都这样做,以尝试防止恶意代码的堆栈破坏。

堆栈朝着堆增长的概念只是教授堆栈概念的简单方式(当然,早期实现确实是这样工作的,因为它很简单(没有必要让自己变得更难,当没有人试图攻击你时))。


@Martin Stack是在堆上创建的?我不明白。操作系统将堆与栈分开,然后将它们分成页面,这不是架构相关的吗?因为我所指的是逻辑地址,而这些逻辑地址只是编译器相关的,对吧? - brett
@Brett:从硬件的角度来看,所有的内存都只是内存。操作系统可以随意选择使用方式。 - Potatoswatter
1
@brett:在旧时代,堆栈和堆是不同的实体,并且在概念上相互增长。但这很容易被破坏并导致恶意代码执行。所以一些聪明人想了一下。现在,堆栈只是一系列帧(每个函数调用一个)。没有技术原因需要它们一个接一个地去(因为当前的帧有一个链接回到上一个)。那么为什么不使用正常的内存管理例程在堆中分配堆栈帧呢?从概念上讲:展开堆栈就是释放当前帧并向后跟随链。 - Martin York
@brett:这可能比那稍微复杂一些,但从概念上讲,每个堆栈帧都可以在堆内分配。只要函数局部变量在退出本地作用域时被正确销毁,堆栈的实现细节对执行代码没有影响。这是经典的面向对象设计(运行时更改了堆栈帧的实现,但代码不需要知道,因为堆栈帧的接口是相同的。唯一受影响的代码是依赖于堆栈的实现细节(例如您的代码))。 - Martin York
@Martin York,您能否指引我一些文章或书籍来解释这个问题? - brett
@brett:没有标准方法。我的意思是你不能假设向上或向下或均匀分布。栈的实际实现方式是由编译器决定的实现细节。你的最佳选择是阅读编译器特定文档,了解如何实现堆栈。 - Martin York

1

这样的实验是不可靠的,因为会遇到异常情况。优化器可能会把你搞糊涂,或者系统可能会使用寄存器作为参数传递。如果您确实必须知道堆栈方向,请阅读处理器的手册。

尽管如此,除非你在写操作系统或者真正低层级的东西,如果你需要知道堆栈方向,那么你可能正在做一些丑陋和可怕的事情,应该重新考虑你的方法。


0
考虑以下重写,它展示了编译器可能实现您的代码的一种方式:
void stackDirection(int* i) { struct __vars_stackDirection { int j; } *__stackframe_stackDirection = malloc(sizeof(int)); }
 if(&(__stackframe.j) > i)
     cout<<"Stack is growing up \n"<<endl;
 else 
     cout<<"Stack is growing down \n"<<endl;

} int main() {
struct __vars_main { int i; } *__stackframe_main = malloc(sizeof(int));

} 主函数 {
struct __vars_main { int i; } *__stackframe_main = malloc(sizeof(int));

  stackDirection(&(__stackframe.i));

}

你必须同意__stackframe_stackDirection__stackframe_main本质上是随机的。堆栈可以向上或向下增长。

更糟糕的是,你假设一个线性模型。要么a<b,b<a或a==b。但对于指针来说,这并不成立。所有三个比较a<b,b<a和a==b可能同时为假。


0

你的函数依赖于指向任何地方的int*参数,并且也可以为NULL。最好比较两个本地变量的地址。


编译器可以自由地按任意顺序分配局部变量。是的,它会按正确的顺序构造和销毁它们,但它可以在任何地方为它们分配空间。 - sharptooth

0

这超出了C++标准的范围。它是一种实现定义行为,可能取决于特定的操作系统/处理器架构等。

最好不要依赖或依赖这些细节,除非你从事道德黑客 :)和/或不太关心可移植性


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接