0

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

#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

4 回答 4

9

您的代码会导致未定义的行为。您不能将指针传递给 anint然后从中减去任意数量并期望它指向有意义的东西。编译器可以按照它喜欢的任何顺序放置a, b, 和c任何它喜欢的地方。他们之间没有任何保证的关系,所以你不能假设someFunction会做任何有意义的事情。

于 2013-09-19T17:07:02.717 回答
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];
}
于 2013-09-19T17:09:16.647 回答
2

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

于 2013-09-19T17:17:59.843 回答
1

正如许多人所回答的那样,您的代码是错误的,因为触发了未定义的行为,另请参见类似问题的答案。

在您的原始代码中,优化编译器可以放置abc在寄存器中重叠它们的堆栈位置等......

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

然后,正确的方法是将这些变量打包在 a struct(或 a 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

您还可以使用数组成员(甚至可能是灵活的或零长度的数组来处理日常事务)#define a _frame.v[0]等等......

实际上,一个好的优化编译器几乎可以像您的原始代码一样优化它。

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

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

有关使用此类技术的两个示例(都是因为需要精确的垃圾收集器),请参阅我的qish 垃圾收集器和MELT (扩展GCC的领域特定语言)的(生成的 C++)代码。另请参阅Chicken SchemeOcaml 运行时约定(及其<caml/memory.h>标头)的(生成的)C 代码。

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

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

于 2013-09-19T17:15:15.060 回答