将变量按顺序保存到堆栈中

时间:2013-09-19 17:05:17

标签: c++ stack

我正在尝试编写一个简单的程序来展示如何在堆栈上间接操作变量。在下面的代码中,一切都按计划工作:即使a的地址被传入,我也可以间接地改变c的值。但是,如果我删除最后一行代码(或最后三行代码),则不再适用。这些行是否以某种方式迫使编译器将我的3个变量按顺序放入堆栈中?我的期望是情况总是如此。

#include <iostream>

using namespace std;

void  someFunction(int* intPtr)
{
  // write some code to break main's critical output
  int* cptr = intPtr - 2;
  *cptr = 0;
} 


int main() 
{
  int a = 1;
  int b = 2;
  int c = 3;

  someFunction(&a);

  cout << a << endl;
  cout << b << endl;
  cout << "Critical value is (must be 3): " << c << endl;

  cout << &a << endl;
  cout << &b << endl;
  cout << &c << endl; //when commented out, critical value is 3
}

4 个答案:

答案 0 :(得分:9)

您的代码会导致未定义的行为。您无法将指针传递给int,然后只需从中减去任意数量,并指望它指向有意义的内容。编译器可以按照自己喜欢的顺序将abc放在任何地方。他们之间没有任何保证的关系,所以你不能假设someFunction会做任何有意义的事情。

答案 1 :(得分:4)

编译器可以将它们放置在当前堆栈帧中的任何位置和任何顺序,如果不使用它甚至可以优化它们。只需让编译器按照您想要的方式执行操作,使用数组,其中指针算法是安全的:

int main() 
{
    int myVars[3] = {1,2,3};

    //In C++, one could use immutable (const) references for convenience,
    //which should be optimized/eliminated pretty well.
    //But I would never ever use them for pointer arithmetic.
    int& const a = myVars[0];
    int& const b = myVars[1];
    int& const c = myVars[2];
}

答案 2 :(得分:2)

你做的是未定义的行为,所以任何事情都可能发生。但是可能会发生的是,当你没有通过注释c来获取cout << &c << endl;的地址时,编译器可以优化avay变量c。然后,它会将cout << c替换为cout << 3

答案 3 :(得分:1)

许多人已经回答,自触发undefined behavior以来,您的代码错误,另请参阅this answer类似的问题。

在原始代码中,优化编译器可以将abc放在寄存器中,重叠其堆栈位置等。 ..

然而,有合理的理由想知道堆栈上局部变量的位置(精确的垃圾收集,内省和反射......)。

然后,正确的方法是将这些变量打包在struct(或class)中,并以某种方式访问​​该结构(例如,将它们链接到列表中等)

所以你的代码可以从

开始
void fun (void)
{
   struct {
     int a;
     int b;
     int c;
   } _frame;
#define a _frame.a
#define b _frame.b
#define c _frame.c
   do_something_with(&_frame); // e.g. link it

您也可以使用数组成员(甚至可能使用flexible or zero-length数组进行内务处理例程)和#define a _frame.v[0]等...

实际上,优秀的优化编译器可以优化原始代码。

可能_frame的类型可能在fun函数之外,并且您将为_frame生成用于检查或垃圾收集的内务处理函数。功能

不要忘记在例程结束时取消链接框架。使用适当的构造函数和析构函数使框架成为对象肯定有帮助。构造函数将链接框架,析构函数将取消链接。

对于使用此类技术的两个示例(因为需要precise garbage collector),请参阅我的qish garbage collectorMELT的(生成的C ++)代码(特定于域的语言)延伸GCC)。另请参阅Chicken SchemeOcaml runtime conventions(及其<caml/memory.h>标题的(生成的)C代码。

在实践中,这种方法对生成的 C或C ++代码更受欢迎(正因为您还将生成内务代码)。如果手动编写它们,请至少考虑花哨的宏(和模板)来帮助您。参见例如gcc/melt-runtime.h

我实际上认为这是C中的缺陷。应该有一些语言特性(和编译器实现)来内省堆栈并对其进行(可移植)回溯。