0

変数をスタック上で間接的に操作する方法を示す簡単なプログラムを作成しようとしています。以下のコードでは、すべてが計画どおりに機能します。a のアドレスが渡されても、c の値を間接的に変更できます。ただし、コードの最後の行 (または最後の 3 行のいずれか) を削除すると、これは適用されなくなります。これらの行は、何らかの形でコンパイラに 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

4 に答える 4

9

あなたのコードは未定義の動作を引き起こします。にポインターを渡してintから、そこから任意の量を差し引いて、意味のあるものを指していると期待することはできません。コンパイラは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

多くの人が答えているように、未定義の動作をトリガーするため、コードが間違っています。同様の質問に対するこの回答も参照してください。

元のコードでは、最適化コンパイラは を配置しa、レジスタにスタックの場所などをオーバーラップさせる可能性があります....bc

ただし、スタック上のローカル変数の場所を知りたい正当な理由があります (正確なガベージ コレクション、イントロスペクション、リフレクションなど)。

struct正しい方法は、これらの変数を a (または 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

ルーチンの最後にフレームのリンクを解除することを忘れないでください。フレームを適切なコンストラクタとデストラクタを持つオブジェクトにすることは間違いなく役に立ちます。コンストラクタはフレームをリンクし、デストラクタはリンクを解除します。

このような手法が使用されている 2 つの例については (どちらも正確なガベージ コレクターが必要なため)、私のqish ガベージ コレクターとMELT ( GCCを拡張するドメイン固有言語) の (生成された C++) コードを参照してください。Chicken Schemeの (生成された) C コードまたはOcaml ランタイム規則(およびその<caml/memory.h>ヘッダー) も参照してください。

実際には、このようなアプローチは、生成されたC または C++ コードに適しています (ハウスキーピング コードも生成するため)。手動で記述する場合は、少なくとも手の込んだマクロ (およびテンプレート) を検討してください。gcc/melt-runtime.hなどを参照してください。

実際、これは C の欠陥であると考えています。スタックをイントロスペクトし、(移植可能に) バックトレースするための言語機能 (およびコンパイラの実装) が必要です。

于 2013-09-19T17:15:15.060 に答える